From eb6a363afd71a85f7c686d2155bb9fdb3deb0bf0 Mon Sep 17 00:00:00 2001 From: gered Date: Thu, 2 Mar 2023 15:10:27 -0500 Subject: [PATCH] formatting note that i'm intentionally not using rustfmt. i've tried to like that tool, but in the end i just really don't like it. too many edge cases and subjectivity and not enough customization. which is probably the intent. which makes me hate it that much more. fuck you, rustfmt. --- examples/audio_playback/src/main.rs | 304 +- examples/balls/src/main.rs | 168 +- examples/balls_v2/src/entities.rs | 336 +- examples/balls_v2/src/main.rs | 34 +- examples/balls_v2/src/states.rs | 142 +- examples/slimed/src/entities/events.rs | 156 +- examples/slimed/src/entities/mod.rs | 880 ++--- examples/slimed/src/entities/systems.rs | 1130 +++---- examples/slimed/src/main.rs | 266 +- examples/slimed/src/states.rs | 314 +- examples/slimed/src/support.rs | 108 +- examples/slimed/src/tilemap.rs | 180 +- examples/template_complicated/src/main.rs | 268 +- examples/template_minimal/src/main.rs | 30 +- libretrogd/benches/bitmap.rs | 32 +- libretrogd/benches/blit.rs | 883 +++-- libretrogd/benches/loading.rs | 24 +- libretrogd/src/audio/buffer/mod.rs | 94 +- libretrogd/src/audio/buffer/wav.rs | 500 +-- libretrogd/src/audio/device.rs | 658 ++-- libretrogd/src/audio/mod.rs | 214 +- libretrogd/src/audio/queue.rs | 508 +-- libretrogd/src/base/mod.rs | 6 +- libretrogd/src/entities/mod.rs | 1299 ++++---- libretrogd/src/events/mod.rs | 526 ++- libretrogd/src/graphics/bitmap/blit.rs | 2176 ++++++------ libretrogd/src/graphics/bitmap/gif.rs | 929 +++--- libretrogd/src/graphics/bitmap/iff.rs | 1058 +++--- libretrogd/src/graphics/bitmap/mod.rs | 972 +++--- libretrogd/src/graphics/bitmap/pcx.rs | 550 ++-- libretrogd/src/graphics/bitmap/primitives.rs | 1080 +++--- libretrogd/src/graphics/bitmapatlas.rs | 262 +- libretrogd/src/graphics/blendmap.rs | 642 ++-- libretrogd/src/graphics/font.rs | 392 +-- libretrogd/src/graphics/palette.rs | 1103 +++---- libretrogd/src/lib.rs | 36 +- libretrogd/src/math/circle.rs | 170 +- libretrogd/src/math/matrix3x3.rs | 734 ++--- libretrogd/src/math/mod.rs | 267 +- libretrogd/src/math/rect.rs | 482 +-- libretrogd/src/math/vector2.rs | 742 ++--- libretrogd/src/states/mod.rs | 2914 ++++++++--------- libretrogd/src/system/event.rs | 304 +- .../system/input_devices/keyboard/codes.rs | 948 +++--- .../src/system/input_devices/keyboard/mod.rs | 124 +- .../input_devices/keyboard/scancodes.rs | 972 +++--- libretrogd/src/system/input_devices/mod.rs | 46 +- .../src/system/input_devices/mouse/buttons.rs | 32 +- .../src/system/input_devices/mouse/mod.rs | 512 +-- libretrogd/src/system/mod.rs | 868 ++--- libretrogd/src/utils/bytes.rs | 14 +- libretrogd/src/utils/io.rs | 22 +- libretrogd/src/utils/lzwgif.rs | 1012 +++--- libretrogd/src/utils/mod.rs | 28 +- libretrogd/src/utils/packbits.rs | 358 +- libretrogd/tests/graphics.rs | 2680 +++++++-------- 56 files changed, 15746 insertions(+), 15743 deletions(-) diff --git a/examples/audio_playback/src/main.rs b/examples/audio_playback/src/main.rs index a3cc579..dd7b3ba 100644 --- a/examples/audio_playback/src/main.rs +++ b/examples/audio_playback/src/main.rs @@ -9,195 +9,193 @@ use libretrogd::utils::rnd_value; #[derive(Debug, Copy, Clone)] struct AudioChannelStatus { - size: usize, - position: usize, - playing: bool + size: usize, + position: usize, + playing: bool, } fn load_and_convert_wav(path: &Path, target_spec: &AudioSpec) -> Result { - let sound = AudioBuffer::load_wav_file(path)?; - let original_spec = *sound.spec(); - let sound = sound.convert(target_spec)?; - let final_spec = *sound.spec(); - if original_spec != final_spec { - println!("{:?} was converted from {:?} to {:?}", path, original_spec, final_spec); - } else { - println!("{:?} did not need to be converted from {:?}", path, original_spec); - } - Ok(sound) + let sound = AudioBuffer::load_wav_file(path)?; + let original_spec = *sound.spec(); + let sound = sound.convert(target_spec)?; + let final_spec = *sound.spec(); + if original_spec != final_spec { + println!("{:?} was converted from {:?} to {:?}", path, original_spec, final_spec); + } else { + println!("{:?} did not need to be converted from {:?}", path, original_spec); + } + Ok(sound) } pub struct SineWaveGenerator { - t: usize, + t: usize, } impl SineWaveGenerator { - pub fn new() -> Self { - SineWaveGenerator { - t: 0 - } - } + pub fn new() -> Self { + SineWaveGenerator { + t: 0 + } + } } impl AudioGenerator for SineWaveGenerator { - fn gen_sample(&mut self, position: usize) -> Option { - const MAX_TIME: usize = AUDIO_FREQUENCY_22KHZ as usize * 3; // 3 seconds - if self.t < MAX_TIME { - let sample = (self.t as f64 * 0.25).sin() * 80.0; - self.t += 1; - Some((sample + 128.0) as u8) - } else { - None - } - } + fn gen_sample(&mut self, position: usize) -> Option { + const MAX_TIME: usize = AUDIO_FREQUENCY_22KHZ as usize * 3; // 3 seconds + if self.t < MAX_TIME { + let sample = (self.t as f64 * 0.25).sin() * 80.0; + self.t += 1; + Some((sample + 128.0) as u8) + } else { + None + } + } } fn main() -> Result<()> { - let mut system = SystemBuilder::new().window_title("Audio Playback").vsync(true).build()?; + let mut system = SystemBuilder::new().window_title("Audio Playback").vsync(true).build()?; - let mut using_queue_commands = false; - let mut volume = 1.0; + let mut using_queue_commands = false; + let mut volume = 1.0; - let sounds = [ - load_and_convert_wav(Path::new("./assets/pickup-coin.wav"), system.audio.spec())?, - load_and_convert_wav(Path::new("./assets/powerup.wav"), system.audio.spec())?, - load_and_convert_wav(Path::new("./assets/explosion.wav"), system.audio.spec())?, - load_and_convert_wav(Path::new("./assets/jump.wav"), system.audio.spec())?, - load_and_convert_wav(Path::new("./assets/laser-shoot.wav"), system.audio.spec())?, - ]; + let sounds = [ + load_and_convert_wav(Path::new("./assets/pickup-coin.wav"), system.audio.spec())?, + load_and_convert_wav(Path::new("./assets/powerup.wav"), system.audio.spec())?, + load_and_convert_wav(Path::new("./assets/explosion.wav"), system.audio.spec())?, + load_and_convert_wav(Path::new("./assets/jump.wav"), system.audio.spec())?, + load_and_convert_wav(Path::new("./assets/laser-shoot.wav"), system.audio.spec())?, + ]; - let mut statuses = [AudioChannelStatus { size: 0, position: 0, playing: false }; NUM_CHANNELS]; + let mut statuses = [AudioChannelStatus { size: 0, position: 0, playing: false }; NUM_CHANNELS]; - while !system.do_events() { - if system.input_devices.keyboard.is_key_pressed(Scancode::Escape) { - break; - } + while !system.do_events() { + if system.input_devices.keyboard.is_key_pressed(Scancode::Escape) { + break; + } - let mut audio_device = system.audio.lock(); - audio_device.volume = volume; + let mut audio_device = system.audio.lock(); + audio_device.volume = volume; - if system.input_devices.keyboard.is_key_pressed(Scancode::Num1) { - if using_queue_commands { - system.audio_queue.play_buffer(&sounds[0], false); - } else { - audio_device.play_buffer(&sounds[0], false)?; - } - } + if system.input_devices.keyboard.is_key_pressed(Scancode::Num1) { + if using_queue_commands { + system.audio_queue.play_buffer(&sounds[0], false); + } else { + audio_device.play_buffer(&sounds[0], false)?; + } + } - if system.input_devices.keyboard.is_key_pressed(Scancode::Num2) { - if using_queue_commands { - system.audio_queue.play_buffer(&sounds[1], false); - } else { - audio_device.play_buffer(&sounds[1], false)?; - } - } + if system.input_devices.keyboard.is_key_pressed(Scancode::Num2) { + if using_queue_commands { + system.audio_queue.play_buffer(&sounds[1], false); + } else { + audio_device.play_buffer(&sounds[1], false)?; + } + } - if system.input_devices.keyboard.is_key_pressed(Scancode::Num3) { - if using_queue_commands { - system.audio_queue.play_buffer(&sounds[2], false); + if system.input_devices.keyboard.is_key_pressed(Scancode::Num3) { + if using_queue_commands { + system.audio_queue.play_buffer(&sounds[2], false); + } else { + audio_device.play_buffer(&sounds[2], false)?; + } + } - } else { - audio_device.play_buffer(&sounds[2], false)?; - } - } + if system.input_devices.keyboard.is_key_pressed(Scancode::Num4) { + if using_queue_commands { + system.audio_queue.play_buffer(&sounds[3], false); + } else { + audio_device.play_buffer(&sounds[3], false)?; + } + } - if system.input_devices.keyboard.is_key_pressed(Scancode::Num4) { - if using_queue_commands { - system.audio_queue.play_buffer(&sounds[3], false); + if system.input_devices.keyboard.is_key_pressed(Scancode::Num5) { + if using_queue_commands { + system.audio_queue.play_buffer(&sounds[4], false); + } else { + audio_device.play_buffer(&sounds[4], false)?; + } + } - } else { - audio_device.play_buffer(&sounds[3], false)?; - } - } + if system.input_devices.keyboard.is_key_pressed(Scancode::Num6) { + if using_queue_commands { + system.audio_queue.play_generator(Box::new(SineWaveGenerator::new()), false); + } else { + audio_device.play_generator(Box::new(SineWaveGenerator::new()), false); + } + } - if system.input_devices.keyboard.is_key_pressed(Scancode::Num5) { - if using_queue_commands { - system.audio_queue.play_buffer(&sounds[4], false); - } else { - audio_device.play_buffer(&sounds[4], false)?; - } - } + if system.input_devices.keyboard.is_key_pressed(Scancode::Num7) { + let index = rnd_value(0, sounds.len() - 1); + if using_queue_commands { + system.audio_queue.play_buffer_on_channel(7, &sounds[index], false)?; + } else { + audio_device.play_buffer_on_channel(7, &sounds[index], false)?; + } + } - if system.input_devices.keyboard.is_key_pressed(Scancode::Num6) { - if using_queue_commands { - system.audio_queue.play_generator(Box::new(SineWaveGenerator::new()), false); - } else { - audio_device.play_generator(Box::new(SineWaveGenerator::new()), false); - } - } + if system.input_devices.keyboard.is_key_pressed(Scancode::S) { + if using_queue_commands { + system.audio_queue.stop_all(); + } else { + audio_device.stop_all(); + } + } - if system.input_devices.keyboard.is_key_pressed(Scancode::Num7) { - let index = rnd_value(0, sounds.len() - 1); - if using_queue_commands { - system.audio_queue.play_buffer_on_channel(7, &sounds[index], false)?; - } else { - audio_device.play_buffer_on_channel(7, &sounds[index], false)?; - } - } + system.audio_queue.apply_to_device(&mut audio_device)?; - if system.input_devices.keyboard.is_key_pressed(Scancode::S) { - if using_queue_commands { - system.audio_queue.stop_all(); - } else { - audio_device.stop_all(); - } - } + if system.input_devices.keyboard.is_key_pressed(Scancode::KpMinus) { + volume -= 0.1; + } + if system.input_devices.keyboard.is_key_pressed(Scancode::KpPlus) { + volume += 0.1; + } + if system.input_devices.keyboard.is_key_pressed(Scancode::Q) { + using_queue_commands = !using_queue_commands; + } - system.audio_queue.apply_to_device(&mut audio_device)?; + for index in 0..NUM_CHANNELS { + let channel = &audio_device[index]; + let mut status = &mut statuses[index]; + status.playing = channel.playing; + status.position = channel.position; + status.size = channel.data.len(); + } - if system.input_devices.keyboard.is_key_pressed(Scancode::KpMinus) { - volume -= 0.1; - } - if system.input_devices.keyboard.is_key_pressed(Scancode::KpPlus) { - volume += 0.1; - } - if system.input_devices.keyboard.is_key_pressed(Scancode::Q) { - using_queue_commands = !using_queue_commands; - } + drop(audio_device); - for index in 0..NUM_CHANNELS { - let channel = &audio_device[index]; - let mut status = &mut statuses[index]; - status.playing = channel.playing; - status.position = channel.position; - status.size = channel.data.len(); - } + system.video.clear(0); - drop(audio_device); + system.video.print_string(&format!("Volume: {:2.2}", volume), 16, 16, FontRenderOpts::Color(10), &system.font); + system.video.print_string( + if using_queue_commands { + "Queueing Commands" + } else { + "Direct Commands" + }, + 160, 16, FontRenderOpts::Color(9), &system.font, + ); - system.video.clear(0); + system.video.print_string("Audio Channels", 16, 32, FontRenderOpts::Color(14), &system.font); - system.video.print_string(&format!("Volume: {:2.2}", volume), 16, 16, FontRenderOpts::Color(10), &system.font); - system.video.print_string( - if using_queue_commands { - "Queueing Commands" - } else { - "Direct Commands" - }, - 160, 16, FontRenderOpts::Color(9), &system.font - ); + let mut y = 48; + for index in 0..NUM_CHANNELS { + let status = &statuses[index]; + system.video.print_string( + &format!( + "channel {} - {} {}", + index, + if status.playing { "playing" } else { "not playing" }, + if status.playing { String::from(format!("{} / {}", status.position, status.size)) } else { String::new() } + ), + 16, y, + FontRenderOpts::Color(15), + &system.font, + ); + y += 16; + } - system.video.print_string("Audio Channels", 16, 32, FontRenderOpts::Color(14), &system.font); + system.display()?; + } - let mut y = 48; - for index in 0..NUM_CHANNELS { - let status = &statuses[index]; - system.video.print_string( - &format!( - "channel {} - {} {}", - index, - if status.playing { "playing" } else { "not playing" }, - if status.playing { String::from(format!("{} / {}", status.position, status.size)) } else { String::new() } - ), - 16, y, - FontRenderOpts::Color(15), - &system.font - ); - y += 16; - } - - system.display()?; - } - - Ok(()) + Ok(()) } diff --git a/examples/balls/src/main.rs b/examples/balls/src/main.rs index 7292ac3..ef67663 100644 --- a/examples/balls/src/main.rs +++ b/examples/balls/src/main.rs @@ -14,105 +14,105 @@ const BALL_WIDTH: u32 = 8; const BALL_HEIGHT: u32 = 8; struct Ball { - x: i32, - y: i32, - dir_x: i32, - dir_y: i32, - sprite: usize, + x: i32, + y: i32, + dir_x: i32, + dir_y: i32, + sprite: usize, } fn main() -> Result<()> { - let mut system = SystemBuilder::new() - .window_title("Flying Balls!") - .vsync(true) - .build()?; + let mut system = SystemBuilder::new() + .window_title("Flying Balls!") + .vsync(true) + .build()?; - let font = BitmaskFont::new_vga_font()?; + let font = BitmaskFont::new_vga_font()?; - let (balls_bmp, balls_palette) = Bitmap::load_pcx_file(Path::new("./assets/balls.pcx"))?; - system.palette = balls_palette.clone(); + let (balls_bmp, balls_palette) = Bitmap::load_pcx_file(Path::new("./assets/balls.pcx"))?; + system.palette = balls_palette.clone(); - let mut sprites = Vec::::new(); - let mut balls = Vec::::new(); + let mut sprites = Vec::::new(); + let mut balls = Vec::::new(); - for i in 0..NUM_BALL_SPRITES { - let mut sprite = Bitmap::new(BALL_WIDTH, BALL_HEIGHT)?; - sprite.blit_region( - BlitMethod::Solid, - &balls_bmp, - &Rect::new(i as i32 * BALL_WIDTH as i32, 0, BALL_WIDTH, BALL_HEIGHT), - 0, - 0, - ); - sprites.push(sprite); - } + for i in 0..NUM_BALL_SPRITES { + let mut sprite = Bitmap::new(BALL_WIDTH, BALL_HEIGHT)?; + sprite.blit_region( + BlitMethod::Solid, + &balls_bmp, + &Rect::new(i as i32 * BALL_WIDTH as i32, 0, BALL_WIDTH, BALL_HEIGHT), + 0, + 0, + ); + sprites.push(sprite); + } - for _ in 0..NUM_BALLS { - let speed = rnd_value(1, 3); - let ball = Ball { - x: rnd_value(0, SCREEN_WIDTH as i32 - 1), - y: rnd_value(0, SCREEN_HEIGHT as i32 - 1), - dir_x: if rnd_value(0, 1) == 0 { -speed } else { speed }, - dir_y: if rnd_value(0, 1) == 0 { -speed } else { speed }, - sprite: rnd_value(0, NUM_BALL_SPRITES - 1), - }; - balls.push(ball); - } + for _ in 0..NUM_BALLS { + let speed = rnd_value(1, 3); + let ball = Ball { + x: rnd_value(0, SCREEN_WIDTH as i32 - 1), + y: rnd_value(0, SCREEN_HEIGHT as i32 - 1), + dir_x: if rnd_value(0, 1) == 0 { -speed } else { speed }, + dir_y: if rnd_value(0, 1) == 0 { -speed } else { speed }, + sprite: rnd_value(0, NUM_BALL_SPRITES - 1), + }; + balls.push(ball); + } - while !system.do_events() { - if system.input_devices.keyboard.is_key_pressed(Scancode::Escape) { - break; - } + while !system.do_events() { + if system.input_devices.keyboard.is_key_pressed(Scancode::Escape) { + break; + } - if system.input_devices.keyboard.is_key_up(Scancode::S) { - for i in 0..NUM_BALLS { - let ball = &mut balls[i]; - ball.x += ball.dir_x; - ball.y += ball.dir_y; + if system.input_devices.keyboard.is_key_up(Scancode::S) { + for i in 0..NUM_BALLS { + let ball = &mut balls[i]; + ball.x += ball.dir_x; + ball.y += ball.dir_y; - if ball.dir_x < 0 { - if ball.x <= 0 { - ball.dir_x = -ball.dir_x; - ball.x = 0; - } - } else { - if ball.x >= (SCREEN_WIDTH - BALL_WIDTH) as i32 { - ball.dir_x = -ball.dir_x; - ball.x = (SCREEN_WIDTH - BALL_WIDTH) as i32; - } - } + if ball.dir_x < 0 { + if ball.x <= 0 { + ball.dir_x = -ball.dir_x; + ball.x = 0; + } + } else { + if ball.x >= (SCREEN_WIDTH - BALL_WIDTH) as i32 { + ball.dir_x = -ball.dir_x; + ball.x = (SCREEN_WIDTH - BALL_WIDTH) as i32; + } + } - if ball.dir_y < 0 { - if ball.y <= 0 { - ball.dir_y = -ball.dir_y; - ball.y = 0; - } - } else { - if ball.y >= (SCREEN_HEIGHT - BALL_HEIGHT) as i32 { - ball.dir_y = -ball.dir_y; - ball.y = (SCREEN_HEIGHT - BALL_HEIGHT) as i32; - } - } - } - } + if ball.dir_y < 0 { + if ball.y <= 0 { + ball.dir_y = -ball.dir_y; + ball.y = 0; + } + } else { + if ball.y >= (SCREEN_HEIGHT - BALL_HEIGHT) as i32 { + ball.dir_y = -ball.dir_y; + ball.y = (SCREEN_HEIGHT - BALL_HEIGHT) as i32; + } + } + } + } - system.video.clear(2); + system.video.clear(2); - system - .video - .print_string("hello, world!", 10, 10, FontRenderOpts::Color(15), &font); + system + .video + .print_string("hello, world!", 10, 10, FontRenderOpts::Color(15), &font); - for i in 0..NUM_BALLS { - system.video.blit( - BlitMethod::Transparent(0), - &sprites[balls[i].sprite], - balls[i].x, - balls[i].y, - ); - } + for i in 0..NUM_BALLS { + system.video.blit( + BlitMethod::Transparent(0), + &sprites[balls[i].sprite], + balls[i].x, + balls[i].y, + ); + } - system.display()?; - } + system.display()?; + } - Ok(()) + Ok(()) } diff --git a/examples/balls_v2/src/entities.rs b/examples/balls_v2/src/entities.rs index 8e34623..5e22642 100644 --- a/examples/balls_v2/src/entities.rs +++ b/examples/balls_v2/src/entities.rs @@ -27,236 +27,236 @@ pub struct Particle; pub struct Color(u8); pub struct LifeLeft { - pub life: f32, - pub initial: f32, + pub life: f32, + pub initial: f32, } pub struct LeavesTrail { - pub timer: f32, + pub timer: f32, } pub struct ColorByLifeTime(u8, u8, u8, u8, u8); pub enum Event { - CollideAgainstEdge(EntityId), - Kill(EntityId), - LeaveTrail(Vector2), + CollideAgainstEdge(EntityId), + Kill(EntityId), + LeaveTrail(Vector2), } fn new_basic_particle_entity(entities: &mut Entities, x: f32, y: f32, color: u8, lifetime: f32, angle: f32, speed: i32) { - let id = entities.new_entity(); - entities.add_component(id, Particle); - entities.add_component(id, Color(color)); - entities.add_component(id, LifeLeft { life: lifetime, initial: lifetime }); - entities.add_component(id, Position(Vector2::new(x, y))); - entities.add_component(id, Velocity(Vector2::from_angle(angle) * speed as f32)); + let id = entities.new_entity(); + entities.add_component(id, Particle); + entities.add_component(id, Color(color)); + entities.add_component(id, LifeLeft { life: lifetime, initial: lifetime }); + entities.add_component(id, Position(Vector2::new(x, y))); + entities.add_component(id, Velocity(Vector2::from_angle(angle) * speed as f32)); } fn new_trail_particle_entity(entities: &mut Entities, x: f32, y: f32, lifetime: f32) { - let id = entities.new_entity(); - entities.add_component(id, Particle); - entities.add_component(id, ColorByLifeTime(33, 26, 21, 16, 10)); - entities.add_component(id, LifeLeft { life: lifetime, initial: lifetime }); - entities.add_component(id, Position(Vector2::new(x, y))); + let id = entities.new_entity(); + entities.add_component(id, Particle); + entities.add_component(id, ColorByLifeTime(33, 26, 21, 16, 10)); + entities.add_component(id, LifeLeft { life: lifetime, initial: lifetime }); + entities.add_component(id, Position(Vector2::new(x, y))); } fn new_bounce_particles(entities: &mut Entities, x: f32, y: f32) { - for direction in 0..6 { - let angle = direction as f32 * (RADIANS_360 / 6.0); - new_basic_particle_entity( - entities, - x, - y, - BOUNCE_PARTICLE_COLOR, - BOUNCE_PARTICLE_LIFETIME, - angle, - BOUNCE_PARTICLE_SPEED - ); - } + for direction in 0..6 { + let angle = direction as f32 * (RADIANS_360 / 6.0); + new_basic_particle_entity( + entities, + x, + y, + BOUNCE_PARTICLE_COLOR, + BOUNCE_PARTICLE_LIFETIME, + angle, + BOUNCE_PARTICLE_SPEED, + ); + } } fn new_ball_entity(entities: &mut Entities) { - let id = entities.new_entity(); + let id = entities.new_entity(); - let x: i32 = rnd_value(SCREEN_LEFT as i32 + 1, SCREEN_RIGHT as i32 - BALL_SIZE - 1); - let y: i32 = rnd_value(SCREEN_TOP as i32 + 1, SCREEN_BOTTOM as i32 - BALL_SIZE - 1); + let x: i32 = rnd_value(SCREEN_LEFT as i32 + 1, SCREEN_RIGHT as i32 - BALL_SIZE - 1); + let y: i32 = rnd_value(SCREEN_TOP as i32 + 1, SCREEN_BOTTOM as i32 - BALL_SIZE - 1); - let speed = rnd_value(1, 3) * BALL_BASE_SPEED; - let vx = if rnd_value(0, 1) == 0 { -speed } else { speed }; - let vy = if rnd_value(0, 1) == 0 { -speed } else { speed }; + let speed = rnd_value(1, 3) * BALL_BASE_SPEED; + let vx = if rnd_value(0, 1) == 0 { -speed } else { speed }; + let vy = if rnd_value(0, 1) == 0 { -speed } else { speed }; - let sprite_index = rnd_value(0, NUM_BALL_SPRITES - 1); + let sprite_index = rnd_value(0, NUM_BALL_SPRITES - 1); - entities.add_component(id, Position(Vector2::new(x as f32, y as f32))); - entities.add_component(id, Velocity(Vector2::new(vx as f32, vy as f32))); - entities.add_component(id, SpriteIndex(sprite_index)); - entities.add_component(id, BouncesAgainstEdge); - entities.add_component(id, LeavesTrail { timer: BALL_TRAIL_PARTICLE_INTERVAL }); + entities.add_component(id, Position(Vector2::new(x as f32, y as f32))); + entities.add_component(id, Velocity(Vector2::new(vx as f32, vy as f32))); + entities.add_component(id, SpriteIndex(sprite_index)); + entities.add_component(id, BouncesAgainstEdge); + entities.add_component(id, LeavesTrail { timer: BALL_TRAIL_PARTICLE_INTERVAL }); } fn update_system_movement(context: &mut Context) { - let mut positions = context.entities.components_mut::().unwrap(); - let velocities = context.entities.components::(); + let mut positions = context.entities.components_mut::().unwrap(); + let velocities = context.entities.components::(); - for (entity, position) in positions.iter_mut() { - if let Some(velocity) = velocities.get(&entity) { - position.0 += velocity.0 * context.delta; - } - } + for (entity, position) in positions.iter_mut() { + if let Some(velocity) = velocities.get(&entity) { + position.0 += velocity.0 * context.delta; + } + } } fn update_system_collision(context: &mut Context) { - let bounceables = context.entities.components::().unwrap(); - let mut positions = context.entities.components_mut::(); - let mut velocities = context.entities.components_mut::(); + let bounceables = context.entities.components::().unwrap(); + let mut positions = context.entities.components_mut::(); + let mut velocities = context.entities.components_mut::(); - for (entity, _) in bounceables.iter() { - let mut position = positions.get_mut(&entity).unwrap(); - let mut velocity = velocities.get_mut(&entity).unwrap(); + for (entity, _) in bounceables.iter() { + let mut position = positions.get_mut(&entity).unwrap(); + let mut velocity = velocities.get_mut(&entity).unwrap(); - let mut bounced = false; - if position.0.x as i32 <= SCREEN_LEFT as i32 || position.0.x as i32 + BALL_SIZE >= SCREEN_RIGHT as i32 { - position.0.x -= velocity.0.x * context.delta; - velocity.0.x = -velocity.0.x; - bounced = true; - } - if position.0.y as i32 <= SCREEN_TOP as i32 || position.0.y as i32 + BALL_SIZE >= SCREEN_BOTTOM as i32 { - position.0.y -= velocity.0.y * context.delta; - velocity.0.y = -velocity.0.y; - bounced = true; - } - if bounced { - context.event_publisher.queue(Event::CollideAgainstEdge(*entity)); - } - } + let mut bounced = false; + if position.0.x as i32 <= SCREEN_LEFT as i32 || position.0.x as i32 + BALL_SIZE >= SCREEN_RIGHT as i32 { + position.0.x -= velocity.0.x * context.delta; + velocity.0.x = -velocity.0.x; + bounced = true; + } + if position.0.y as i32 <= SCREEN_TOP as i32 || position.0.y as i32 + BALL_SIZE >= SCREEN_BOTTOM as i32 { + position.0.y -= velocity.0.y * context.delta; + velocity.0.y = -velocity.0.y; + bounced = true; + } + if bounced { + context.event_publisher.queue(Event::CollideAgainstEdge(*entity)); + } + } } fn update_system_lifetime(context: &mut Context) { - let mut lifetimes = context.entities.components_mut::().unwrap(); - for (entity, lifetime) in lifetimes.iter_mut() { - lifetime.life -= context.delta; - if lifetime.life < 0.0 { - context.event_publisher.queue(Event::Kill(*entity)); - } - } + let mut lifetimes = context.entities.components_mut::().unwrap(); + for (entity, lifetime) in lifetimes.iter_mut() { + lifetime.life -= context.delta; + if lifetime.life < 0.0 { + context.event_publisher.queue(Event::Kill(*entity)); + } + } } fn update_system_leave_particle_trail(context: &mut Context) { - let mut leaves_trails = context.entities.components_mut::().unwrap(); - let positions = context.entities.components::(); + let mut leaves_trails = context.entities.components_mut::().unwrap(); + let positions = context.entities.components::(); - for (entity, leaves_trail) in leaves_trails.iter_mut() { - leaves_trail.timer -= context.delta; + for (entity, leaves_trail) in leaves_trails.iter_mut() { + leaves_trail.timer -= context.delta; - if leaves_trail.timer <= 0.0 { - leaves_trail.timer = BALL_TRAIL_PARTICLE_INTERVAL; - let position = positions.get(&entity).unwrap(); - let mut trail_position = position.0; - trail_position.x += (BALL_SIZE / 2) as f32; - trail_position.y += (BALL_SIZE / 2) as f32; - context.event_publisher.queue(Event::LeaveTrail(trail_position)); - } - } + if leaves_trail.timer <= 0.0 { + leaves_trail.timer = BALL_TRAIL_PARTICLE_INTERVAL; + let position = positions.get(&entity).unwrap(); + let mut trail_position = position.0; + trail_position.x += (BALL_SIZE / 2) as f32; + trail_position.y += (BALL_SIZE / 2) as f32; + context.event_publisher.queue(Event::LeaveTrail(trail_position)); + } + } } fn render_system_sprites(context: &mut Context) { - let sprite_indices = context.entities.components::().unwrap(); - let positions = context.entities.components::(); + let sprite_indices = context.entities.components::().unwrap(); + let positions = context.entities.components::(); - for (entity, sprite_index) in sprite_indices.iter() { - let position = positions.get(&entity).unwrap(); - context.system.video.blit( - BlitMethod::Transparent(0), - &context.sprites[sprite_index.0], - position.0.x as i32, - position.0.y as i32 - ); - } + for (entity, sprite_index) in sprite_indices.iter() { + let position = positions.get(&entity).unwrap(); + context.system.video.blit( + BlitMethod::Transparent(0), + &context.sprites[sprite_index.0], + position.0.x as i32, + position.0.y as i32, + ); + } } fn render_system_particles(context: &mut Context) { - let particles = context.entities.components::().unwrap(); - let positions = context.entities.components::(); - let colors = context.entities.components::(); - let colors_by_lifetime = context.entities.components::(); - let lifetimes = context.entities.components::(); + let particles = context.entities.components::().unwrap(); + let positions = context.entities.components::(); + let colors = context.entities.components::(); + let colors_by_lifetime = context.entities.components::(); + let lifetimes = context.entities.components::(); - for (entity, _) in particles.iter() { - let position = positions.get(&entity).unwrap(); + for (entity, _) in particles.iter() { + let position = positions.get(&entity).unwrap(); - let pixel_color; - if let Some(color) = colors.get(&entity) { - pixel_color = Some(color.0); - } else if let Some(color_by_lifetime) = colors_by_lifetime.get(&entity) { - let lifetime = lifetimes.get(&entity).unwrap(); - let percent_life = lifetime.life / lifetime.initial; - pixel_color = Some(if percent_life >= 0.8 { - color_by_lifetime.0 - } else if percent_life >= 0.6 { - color_by_lifetime.1 - } else if percent_life >= 0.4 { - color_by_lifetime.2 - } else if percent_life >= 0.2 { - color_by_lifetime.3 - } else { - color_by_lifetime.4 - }); - } else { - pixel_color = None; - } + let pixel_color; + if let Some(color) = colors.get(&entity) { + pixel_color = Some(color.0); + } else if let Some(color_by_lifetime) = colors_by_lifetime.get(&entity) { + let lifetime = lifetimes.get(&entity).unwrap(); + let percent_life = lifetime.life / lifetime.initial; + pixel_color = Some(if percent_life >= 0.8 { + color_by_lifetime.0 + } else if percent_life >= 0.6 { + color_by_lifetime.1 + } else if percent_life >= 0.4 { + color_by_lifetime.2 + } else if percent_life >= 0.2 { + color_by_lifetime.3 + } else { + color_by_lifetime.4 + }); + } else { + pixel_color = None; + } - if let Some(color) = pixel_color { - context.system.video.set_pixel(position.0.x as i32, position.0.y as i32, color); - } - } + if let Some(color) = pixel_color { + context.system.video.set_pixel(position.0.x as i32, position.0.y as i32, color); + } + } } fn event_handler(event: &Event, context: &mut Context) -> bool { - match event { - Event::Kill(entity) => { - context.entities.remove_entity(*entity); - }, - Event::CollideAgainstEdge(entity) => { - let positions = context.entities.components::(); - let position = positions.get(entity).unwrap(); - let x = position.0.x + (BALL_SIZE / 2) as f32; - let y = position.0.y + (BALL_SIZE / 2) as f32; - drop(positions); - new_bounce_particles(&mut context.entities, x, y); - }, - Event::LeaveTrail(position) => { - new_trail_particle_entity(&mut context.entities, position.x, position.y, TRAIL_PARTICLE_LIFETIME); - } - } - false + match event { + Event::Kill(entity) => { + context.entities.remove_entity(*entity); + } + Event::CollideAgainstEdge(entity) => { + let positions = context.entities.components::(); + let position = positions.get(entity).unwrap(); + let x = position.0.x + (BALL_SIZE / 2) as f32; + let y = position.0.y + (BALL_SIZE / 2) as f32; + drop(positions); + new_bounce_particles(&mut context.entities, x, y); + } + Event::LeaveTrail(position) => { + new_trail_particle_entity(&mut context.entities, position.x, position.y, TRAIL_PARTICLE_LIFETIME); + } + } + false } pub fn init_entities(entities: &mut Entities) { - entities.init_components::(); - entities.init_components::(); - entities.init_components::(); - entities.init_components::(); - entities.init_components::(); - entities.init_components::(); - entities.init_components::(); - entities.init_components::(); - entities.init_components::(); + entities.init_components::(); + entities.init_components::(); + entities.init_components::(); + entities.init_components::(); + entities.init_components::(); + entities.init_components::(); + entities.init_components::(); + entities.init_components::(); + entities.init_components::(); - entities.remove_all_entities(); - for _ in 0..NUM_BALLS { - new_ball_entity(entities); - } + entities.remove_all_entities(); + for _ in 0..NUM_BALLS { + new_ball_entity(entities); + } } pub fn init_component_system(cs: &mut ComponentSystems) { - cs.add_update_system(update_system_movement); - cs.add_update_system(update_system_collision); - cs.add_update_system(update_system_lifetime); - cs.add_update_system(update_system_leave_particle_trail); - cs.add_render_system(render_system_particles); - cs.add_render_system(render_system_sprites); + cs.add_update_system(update_system_movement); + cs.add_update_system(update_system_collision); + cs.add_update_system(update_system_lifetime); + cs.add_update_system(update_system_leave_particle_trail); + cs.add_render_system(render_system_particles); + cs.add_render_system(render_system_sprites); } pub fn init_event_listeners(event_listeners: &mut EventListeners) { - event_listeners.add(event_handler); + event_listeners.add(event_handler); } diff --git a/examples/balls_v2/src/main.rs b/examples/balls_v2/src/main.rs index 286892e..6f53ae4 100644 --- a/examples/balls_v2/src/main.rs +++ b/examples/balls_v2/src/main.rs @@ -12,27 +12,27 @@ mod entities; mod states; fn main() -> Result<()> { - let system = SystemBuilder::new().window_title("Flying Balls").vsync(true).build()?; - let mut game = Game::new(system)?; - let mut states = States::new(); - states.push(SimulationState)?; + let system = SystemBuilder::new().window_title("Flying Balls").vsync(true).build()?; + let mut game = Game::new(system)?; + let mut states = States::new(); + states.push(SimulationState)?; - let tick_frequency = game.context.system.tick_frequency(); - let mut last_ticks = game.context.system.ticks(); + let tick_frequency = game.context.system.tick_frequency(); + let mut last_ticks = game.context.system.ticks(); - while !game.context.system.do_events() && !states.is_empty() { - let ticks = game.context.system.ticks(); - let elapsed = ticks - last_ticks; - last_ticks = ticks; - game.context.delta = (elapsed as f64 / tick_frequency as f64) as f32; + while !game.context.system.do_events() && !states.is_empty() { + let ticks = game.context.system.ticks(); + let elapsed = ticks - last_ticks; + last_ticks = ticks; + game.context.delta = (elapsed as f64 / tick_frequency as f64) as f32; - states.update(&mut game)?; + states.update(&mut game)?; - game.context.system.video.clear(0); - states.render(&mut game); + game.context.system.video.clear(0); + states.render(&mut game); - game.context.system.display()?; - } + game.context.system.display()?; + } - Ok(()) + Ok(()) } diff --git a/examples/balls_v2/src/states.rs b/examples/balls_v2/src/states.rs index 7b4dbf5..ef8eff3 100644 --- a/examples/balls_v2/src/states.rs +++ b/examples/balls_v2/src/states.rs @@ -12,97 +12,97 @@ pub const NUM_BALLS: usize = 32; pub const NUM_BALL_SPRITES: usize = 16; pub struct Context { - pub delta: f32, - pub system: System, - pub font: BitmaskFont, - pub sprites: Vec, - pub entities: Entities, - pub event_publisher: EventPublisher, + pub delta: f32, + pub system: System, + pub font: BitmaskFont, + pub sprites: Vec, + pub entities: Entities, + pub event_publisher: EventPublisher, } pub struct Game { - pub context: Context, - pub component_systems: ComponentSystems, - pub event_listeners: EventListeners, + pub context: Context, + pub component_systems: ComponentSystems, + pub event_listeners: EventListeners, } impl Game { - pub fn new(mut system: System) -> Result { - let font = BitmaskFont::new_vga_font()?; + pub fn new(mut system: System) -> Result { + let font = BitmaskFont::new_vga_font()?; - let (balls_bmp, balls_palette) = Bitmap::load_pcx_file(Path::new("./assets/balls.pcx"))?; - system.palette = balls_palette.clone(); + let (balls_bmp, balls_palette) = Bitmap::load_pcx_file(Path::new("./assets/balls.pcx"))?; + system.palette = balls_palette.clone(); - let mut sprites = Vec::new(); - for i in 0..NUM_BALL_SPRITES { - let mut sprite = Bitmap::new(BALL_SIZE as u32, BALL_SIZE as u32)?; - sprite.blit_region( - BlitMethod::Solid, - &balls_bmp, - &Rect::new(i as i32 * BALL_SIZE as i32, 0, BALL_SIZE as u32, BALL_SIZE as u32), - 0, - 0 - ); - sprites.push(sprite); - } + let mut sprites = Vec::new(); + for i in 0..NUM_BALL_SPRITES { + let mut sprite = Bitmap::new(BALL_SIZE as u32, BALL_SIZE as u32)?; + sprite.blit_region( + BlitMethod::Solid, + &balls_bmp, + &Rect::new(i as i32 * BALL_SIZE as i32, 0, BALL_SIZE as u32, BALL_SIZE as u32), + 0, + 0, + ); + sprites.push(sprite); + } - let entities = Entities::new(); - let mut component_systems = ComponentSystems::new(); - let event_publisher = EventPublisher::new(); - let mut event_listeners = EventListeners::new(); + let entities = Entities::new(); + let mut component_systems = ComponentSystems::new(); + let event_publisher = EventPublisher::new(); + let mut event_listeners = EventListeners::new(); - init_component_system(&mut component_systems); - init_event_listeners(&mut event_listeners); + init_component_system(&mut component_systems); + init_event_listeners(&mut event_listeners); - Ok(Game { - context: Context { - delta: 0.0, - system, - font, - sprites, - entities, - event_publisher - }, - component_systems, - event_listeners - }) - } + Ok(Game { + context: Context { + delta: 0.0, + system, + font, + sprites, + entities, + event_publisher, + }, + component_systems, + event_listeners, + }) + } - pub fn do_events(&mut self) { - self.event_listeners.take_queue_from(&mut self.context.event_publisher); - self.event_listeners.dispatch_queue(&mut self.context); - } + pub fn do_events(&mut self) { + self.event_listeners.take_queue_from(&mut self.context.event_publisher); + self.event_listeners.dispatch_queue(&mut self.context); + } } pub struct SimulationState; impl AppState for SimulationState { - fn update(&mut self, _state: State, context: &mut Game) -> Option> { - if context.context.system.input_devices.keyboard.is_key_up(Scancode::S) { - context.do_events(); - context.component_systems.update(&mut context.context); - } + fn update(&mut self, _state: State, context: &mut Game) -> Option> { + if context.context.system.input_devices.keyboard.is_key_up(Scancode::S) { + context.do_events(); + context.component_systems.update(&mut context.context); + } - if context.context.system.input_devices.keyboard.is_key_pressed(Scancode::Escape) { - return Some(StateChange::Pop(1)); - } + if context.context.system.input_devices.keyboard.is_key_pressed(Scancode::Escape) { + return Some(StateChange::Pop(1)); + } - None - } + None + } - fn render(&mut self, _state: State, context: &mut Game) { - context.context.system.video.clear(2); - context.component_systems.render(&mut context.context); - context.context.system.video.print_string("hello, world!", 10, 10, FontRenderOpts::Color(15), &context.context.font); - } + fn render(&mut self, _state: State, context: &mut Game) { + context.context.system.video.clear(2); + context.component_systems.render(&mut context.context); + context.context.system.video.print_string("hello, world!", 10, 10, FontRenderOpts::Color(15), &context.context.font); + } - fn transition(&mut self, _state: State, _context: &mut Game) -> bool { - true - } + fn transition(&mut self, _state: State, _context: &mut Game) -> bool { + true + } - fn state_change(&mut self, new_state: State, _old_state: State, context: &mut Game) { - if new_state == State::Pending { - init_entities(&mut context.context.entities); - } - } + fn state_change(&mut self, new_state: State, _old_state: State, context: &mut Game) { + if new_state == State::Pending { + init_entities(&mut context.context.entities); + } + } } \ No newline at end of file diff --git a/examples/slimed/src/entities/events.rs b/examples/slimed/src/entities/events.rs index 82658c5..f4bccde 100644 --- a/examples/slimed/src/entities/events.rs +++ b/examples/slimed/src/entities/events.rs @@ -6,88 +6,88 @@ use crate::entities::*; #[derive(Debug, Copy, Clone)] pub enum Event { - TurnAndMove(EntityId, Direction), - MoveForward(EntityId), - Remove(EntityId), - RemoveAttachment(EntityId), - AnimationFinished(EntityId), - Spawn(EntityId), - SpawnSlimeRandomly, - SetActivity(EntityId, EntityActivity), - Attack(EntityId), - Hit(EntityId, EntityId, i32, Vector2), - Kill(EntityId), - Pickup(EntityId, EntityId), + TurnAndMove(EntityId, Direction), + MoveForward(EntityId), + Remove(EntityId), + RemoveAttachment(EntityId), + AnimationFinished(EntityId), + Spawn(EntityId), + SpawnSlimeRandomly, + SetActivity(EntityId, EntityActivity), + Attack(EntityId), + Hit(EntityId, EntityId, i32, Vector2), + Kill(EntityId), + Pickup(EntityId, EntityId), } fn event_handler(event: &Event, context: &mut Core) -> bool { - match event { - Event::Remove(entity) => { - if context.entities.has_entity(*entity) { - remove_entity(&mut context.entities, *entity); - } - }, - Event::RemoveAttachment(entity) => { - if context.entities.has_entity(*entity) { - remove_entity_attachment(&mut context.entities, *entity); - } - } - Event::TurnAndMove(entity, direction) => { - if context.entities.has_entity(*entity) { - turn_and_move_entity(context, *entity, *direction); - } - }, - Event::MoveForward(entity) => { - if context.entities.has_entity(*entity) { - move_entity_forward(context, *entity); - } - }, - Event::Spawn(entity) => { - // todo - }, - Event::AnimationFinished(entity) => { - if context.entities.has_entity(*entity) { - // if the entity's 'attack' animation just finished, move them back to 'idle' - let activities = context.entities.components::(); - if let Some(activity) = activities.get(entity) { - if activity.0 == EntityActivity::Attacking { - drop(activities); - stop_attack(context, *entity); - } - } - } - } - Event::SpawnSlimeRandomly => { - spawn_slime_randomly(context); - }, - Event::SetActivity(entity, activity) => { - if context.entities.has_entity(*entity) { - set_entity_activity(&mut context.entities, *entity, *activity); - } - }, - Event::Attack(entity) => { - if context.entities.has_entity(*entity) { - attack(context, *entity); - } - }, - Event::Hit(target, source, damage, damage_position) => { - if context.entities.has_entity(*target) { - hit_entity(context, *target, *source, *damage, *damage_position); - } - }, - Event::Kill(entity) => { - kill_entity(context, *entity); - }, - Event::Pickup(picked_up_by, picked_up) => { - if context.entities.has_entity(*picked_up_by) && context.entities.has_entity(*picked_up) { - pickup(context, *picked_up_by, *picked_up); - } - } - } - false + match event { + Event::Remove(entity) => { + if context.entities.has_entity(*entity) { + remove_entity(&mut context.entities, *entity); + } + } + Event::RemoveAttachment(entity) => { + if context.entities.has_entity(*entity) { + remove_entity_attachment(&mut context.entities, *entity); + } + } + Event::TurnAndMove(entity, direction) => { + if context.entities.has_entity(*entity) { + turn_and_move_entity(context, *entity, *direction); + } + } + Event::MoveForward(entity) => { + if context.entities.has_entity(*entity) { + move_entity_forward(context, *entity); + } + } + Event::Spawn(entity) => { + // todo + } + Event::AnimationFinished(entity) => { + if context.entities.has_entity(*entity) { + // if the entity's 'attack' animation just finished, move them back to 'idle' + let activities = context.entities.components::(); + if let Some(activity) = activities.get(entity) { + if activity.0 == EntityActivity::Attacking { + drop(activities); + stop_attack(context, *entity); + } + } + } + } + Event::SpawnSlimeRandomly => { + spawn_slime_randomly(context); + } + Event::SetActivity(entity, activity) => { + if context.entities.has_entity(*entity) { + set_entity_activity(&mut context.entities, *entity, *activity); + } + } + Event::Attack(entity) => { + if context.entities.has_entity(*entity) { + attack(context, *entity); + } + } + Event::Hit(target, source, damage, damage_position) => { + if context.entities.has_entity(*target) { + hit_entity(context, *target, *source, *damage, *damage_position); + } + } + Event::Kill(entity) => { + kill_entity(context, *entity); + } + Event::Pickup(picked_up_by, picked_up) => { + if context.entities.has_entity(*picked_up_by) && context.entities.has_entity(*picked_up) { + pickup(context, *picked_up_by, *picked_up); + } + } + } + false } pub fn init_events(event_listener: &mut EventListeners) { - event_listener.clear(); - event_listener.add(event_handler); + event_listener.clear(); + event_listener.add(event_handler); } \ No newline at end of file diff --git a/examples/slimed/src/entities/mod.rs b/examples/slimed/src/entities/mod.rs index 3af0e80..8e52daa 100644 --- a/examples/slimed/src/entities/mod.rs +++ b/examples/slimed/src/entities/mod.rs @@ -27,77 +27,77 @@ pub const PICKUP_PRE_TIMER: f32 = 0.5; #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub enum EntityActivity { - Idle, - Walking, - Attacking, - Dead, + Idle, + Walking, + Attacking, + Dead, } #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub enum Direction { - South = 0, - West = 1, - East = 2, - North = 3, + South = 0, + West = 1, + East = 2, + North = 3, } impl Direction { - pub fn new_random() -> Self { - use Direction::*; - match rnd_value(0, 3) { - 0 => South, - 1 => West, - 2 => East, - 3 => North, - _ => panic!("unknown random direction!") - } - } + pub fn new_random() -> Self { + use Direction::*; + match rnd_value(0, 3) { + 0 => South, + 1 => West, + 2 => East, + 3 => North, + _ => panic!("unknown random direction!") + } + } } #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub enum SlimeColor { - Green = 0, - Blue = 1, - Orange = 2, + Green = 0, + Blue = 1, + Orange = 2, } impl SlimeColor { - pub fn new_random() -> Self { - use SlimeColor::*; - match rnd_value(0, 2) { - 0 => Green, - 1 => Blue, - 2 => Orange, - _ => panic!("unknown random slime color!") - } - } + pub fn new_random() -> Self { + use SlimeColor::*; + match rnd_value(0, 2) { + 0 => Green, + 1 => Blue, + 2 => Orange, + _ => panic!("unknown random slime color!") + } + } } #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub enum PickupType { - GreenGem, - BlueGem, - OrangeGem, - Coin, + GreenGem, + BlueGem, + OrangeGem, + Coin, } impl PickupType { - pub fn new_random() -> Self { - use PickupType::*; - match rnd_value(0, 3) { - 0 => GreenGem, - 1 => BlueGem, - 2 => OrangeGem, - 3 => Coin, - _ => panic!("unknown random pickup type!") - } - } + pub fn new_random() -> Self { + use PickupType::*; + match rnd_value(0, 3) { + 0 => GreenGem, + 1 => BlueGem, + 2 => OrangeGem, + 3 => Coin, + _ => panic!("unknown random pickup type!") + } + } } #[derive(Debug, Copy, Clone, PartialEq)] pub struct Force { - pub force: Vector2, - pub dissipation_factor: f32, + pub force: Vector2, + pub dissipation_factor: f32, } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -110,57 +110,57 @@ pub struct Activity(pub EntityActivity); #[derive(Debug)] pub struct AnimationDef { - pub frames: &'static [usize], - pub loops: bool, - pub delay: f32, - pub multi_direction_offset: Option, + pub frames: &'static [usize], + pub loops: bool, + pub delay: f32, + pub multi_direction_offset: Option, } impl AnimationDef { - #[inline] - pub fn new(frames: &'static [usize], loops: bool, delay: f32, multi_direction_offset: Option) -> Self { - AnimationDef { - frames, - loops, - delay, - multi_direction_offset, - } - } + #[inline] + pub fn new(frames: &'static [usize], loops: bool, delay: f32, multi_direction_offset: Option) -> Self { + AnimationDef { + frames, + loops, + delay, + multi_direction_offset, + } + } } #[derive(Debug)] pub struct AnimationInstance { - pub def: Rc, - pub frame_index: usize, - pub frame_timer: f32, - pub complete: bool, - pub delay_override: Option, + pub def: Rc, + pub frame_index: usize, + pub frame_timer: f32, + pub complete: bool, + pub delay_override: Option, } impl AnimationInstance { - #[inline] - pub fn from(def: Rc) -> Self { - AnimationInstance { - def, - frame_index: 0, - frame_timer: 0.0, - complete: false, - delay_override: None, - } - } + #[inline] + pub fn from(def: Rc) -> Self { + AnimationInstance { + def, + frame_index: 0, + frame_timer: 0.0, + complete: false, + delay_override: None, + } + } - #[inline] - pub fn change_to(&mut self, def: Rc) { - self.def = def; - self.reset(); - } + #[inline] + pub fn change_to(&mut self, def: Rc) { + self.def = def; + self.reset(); + } - #[inline] - pub fn reset(&mut self) { - self.frame_index = 0; - self.frame_timer = 0.0; - self.complete = false; - } + #[inline] + pub fn reset(&mut self) { + self.frame_index = 0; + self.frame_timer = 0.0; + self.complete = false; + } } pub struct AnimateByActivity(pub Rc>>); @@ -172,40 +172,40 @@ pub struct Position(pub Vector2); pub struct Velocity(pub Vector2); pub struct Forces { - pub forces: Vec, + pub forces: Vec, } impl Forces { - pub fn new() -> Self { - Forces { - forces: Vec::with_capacity(5), - } - } + pub fn new() -> Self { + Forces { + forces: Vec::with_capacity(5), + } + } - pub fn current_force(&self) -> Vector2 { - let mut total_force = Vector2::ZERO; - for force in self.forces.iter() { - total_force += force.force; - } - total_force - } + pub fn current_force(&self) -> Vector2 { + let mut total_force = Vector2::ZERO; + for force in self.forces.iter() { + total_force += force.force; + } + total_force + } - pub fn add(&mut self, force: Vector2, dissipation_factor: f32) { - self.forces.push(Force { force, dissipation_factor }); - } + pub fn add(&mut self, force: Vector2, dissipation_factor: f32) { + self.forces.push(Force { force, dissipation_factor }); + } - pub fn decay(&mut self) { - for force in self.forces.iter_mut() { - force.force *= force.dissipation_factor; - } - self.forces.retain(|f| !f.force.almost_zero(0.001)); - } + pub fn decay(&mut self) { + for force in self.forces.iter_mut() { + force.force *= force.dissipation_factor; + } + self.forces.retain(|f| !f.force.almost_zero(0.001)); + } } pub struct Bounds { - pub width: u32, - pub height: u32, - pub radius: u32, + pub width: u32, + pub height: u32, + pub radius: u32, } pub struct FacingDirection(pub Direction); @@ -221,34 +221,34 @@ pub struct LifeTime(pub f32); pub struct Pixel(pub u8); pub struct Sprite { - pub atlas: Rc, - pub index: usize, + pub atlas: Rc, + pub index: usize, } pub struct RandomlyWalksAround { - pub min_walk_time: f32, - pub max_walk_time: f32, - pub chance_to_move: u32, - pub min_cooldown: f32, - pub max_cooldown: f32, - pub cooldown_timer: f32, + pub min_walk_time: f32, + pub max_walk_time: f32, + pub chance_to_move: u32, + pub min_cooldown: f32, + pub max_cooldown: f32, + pub cooldown_timer: f32, } impl RandomlyWalksAround { - pub fn new(min_walk_time: f32, max_walk_time: f32, chance_to_move: u32, min_cooldown: f32, max_cooldown: f32) -> Self { - RandomlyWalksAround { - min_walk_time, - max_walk_time, - chance_to_move, - min_cooldown, - max_cooldown, - cooldown_timer: 0.0, - } - } + pub fn new(min_walk_time: f32, max_walk_time: f32, chance_to_move: u32, min_cooldown: f32, max_cooldown: f32) -> Self { + RandomlyWalksAround { + min_walk_time, + max_walk_time, + chance_to_move, + min_cooldown, + max_cooldown, + cooldown_timer: 0.0, + } + } - pub fn should_start_walking(&self) -> bool { - rnd_value(0, 100) < self.chance_to_move - } + pub fn should_start_walking(&self) -> bool { + rnd_value(0, 100) < self.chance_to_move + } } pub struct WalkingTime(pub f32); @@ -258,46 +258,46 @@ pub struct MovementSpeed(pub f32); pub struct World; pub struct SpawnTimer { - pub timer: f32, - pub min_time: f32, - pub max_time: f32, - pub max_allowed: usize, + pub timer: f32, + pub min_time: f32, + pub max_time: f32, + pub max_allowed: usize, } impl SpawnTimer { - pub fn new(min_time: f32, max_time: f32, max_allowed: usize) -> Self { - SpawnTimer { - timer: 0.0, - min_time, - max_time, - max_allowed, - } - } + pub fn new(min_time: f32, max_time: f32, max_allowed: usize) -> Self { + SpawnTimer { + timer: 0.0, + min_time, + max_time, + max_allowed, + } + } - pub fn reset_timer(&mut self) { - self.timer = rnd_value(self.min_time, self.max_time); - } + pub fn reset_timer(&mut self) { + self.timer = rnd_value(self.min_time, self.max_time); + } } pub struct Camera { - pub x: i32, - pub y: i32, + pub x: i32, + pub y: i32, } pub struct Pushable; pub struct Pusher { - pub strength: f32, - pub push_force_dissipation: f32, + pub strength: f32, + pub push_force_dissipation: f32, } impl Pusher { - pub fn new() -> Self { - Pusher { - strength: DEFAULT_PUSH_STRENGTH, - push_force_dissipation: DEFAULT_PUSH_DISSIPATION, - } - } + pub fn new() -> Self { + Pusher { + strength: DEFAULT_PUSH_STRENGTH, + push_force_dissipation: DEFAULT_PUSH_DISSIPATION, + } + } } pub struct AttachedTo(pub EntityId); @@ -307,76 +307,76 @@ pub struct Attachment(pub EntityId); pub struct AttachmentOffset(pub Vector2); pub struct AttachmentOffsetByDirection { - pub offsets: [Vector2; 4], + pub offsets: [Vector2; 4], } pub struct Attackable; pub struct SpriteIndexByDirection { - pub base_index: usize, + pub base_index: usize, } pub struct Weapon { - pub atlas: Rc, - pub base_index: usize, - pub offsets: [Vector2; 4], - pub damage: i32, - pub radius_of_effect: u32, + pub atlas: Rc, + pub base_index: usize, + pub offsets: [Vector2; 4], + pub damage: i32, + pub radius_of_effect: u32, } pub struct Life(pub i32); pub enum FlickerMethod { - Color(u8), - OnOff, + Color(u8), + OnOff, } pub struct TimedFlicker { - pub method: FlickerMethod, - pub flick: bool, - pub pre_timer: Option, - pub timer: f32, + pub method: FlickerMethod, + pub flick: bool, + pub pre_timer: Option, + pub timer: f32, } impl TimedFlicker { - pub fn new(timer: f32, method: FlickerMethod) -> Self { - TimedFlicker { - timer, - method, - pre_timer: None, - flick: true, - } - } + pub fn new(timer: f32, method: FlickerMethod) -> Self { + TimedFlicker { + timer, + method, + pre_timer: None, + flick: true, + } + } - pub fn new_with_pre_timer(timer: f32, pre_timer: f32, method: FlickerMethod) -> Self { - TimedFlicker { - timer, - method, - pre_timer: Some(pre_timer), - flick: true, - } - } + pub fn new_with_pre_timer(timer: f32, pre_timer: f32, method: FlickerMethod) -> Self { + TimedFlicker { + timer, + method, + pre_timer: Some(pre_timer), + flick: true, + } + } - pub fn update(&mut self, delta: f32) { - if let Some(mut pre_timer) = self.pre_timer { - pre_timer -= delta; - if pre_timer <= 0.0 { - self.pre_timer = None; - } else { - self.pre_timer = Some(pre_timer); - } - } else { - self.timer -= delta; - self.flick = !self.flick; - } - } + pub fn update(&mut self, delta: f32) { + if let Some(mut pre_timer) = self.pre_timer { + pre_timer -= delta; + if pre_timer <= 0.0 { + self.pre_timer = None; + } else { + self.pre_timer = Some(pre_timer); + } + } else { + self.timer -= delta; + self.flick = !self.flick; + } + } } pub struct HitParticleColor(pub u8); pub struct Pickupable { - pub kind: PickupType, - pub pre_timer: f32, + pub kind: PickupType, + pub pre_timer: f32, } pub struct Pickuper; @@ -384,320 +384,320 @@ pub struct Pickuper; /////////////////////////////////////////////////////////////////////////////////////////////////// pub fn init_everything(context: &mut Game, map_file: &Path, min_spawn_time: f32, max_spawn_time: f32, max_slimes: usize) { - init_entities(&mut context.core.entities); - init_component_system(&mut context.support.component_systems); - init_events(&mut context.support.event_listeners); - context.core.event_publisher.clear(); + init_entities(&mut context.core.entities); + init_component_system(&mut context.support.component_systems); + init_events(&mut context.support.event_listeners); + context.core.event_publisher.clear(); - context.core.tilemap = TileMap::load_from(map_file).unwrap(); - new_camera_entity(&mut context.core, 0, 0); - new_world_entity(&mut context.core, min_spawn_time, max_spawn_time, max_slimes); + context.core.tilemap = TileMap::load_from(map_file).unwrap(); + new_camera_entity(&mut context.core, 0, 0); + new_world_entity(&mut context.core, min_spawn_time, max_spawn_time, max_slimes); } pub fn init_entities(entities: &mut Entities) { - entities.init_components::(); - entities.init_components::(); - entities.init_components::(); - entities.init_components::(); - entities.init_components::(); - entities.init_components::(); - entities.init_components::(); - entities.init_components::(); - entities.init_components::(); - entities.init_components::(); - entities.init_components::(); - entities.init_components::(); - entities.init_components::(); - entities.init_components::(); - entities.init_components::(); - entities.init_components::(); - entities.init_components::(); - entities.init_components::(); - entities.init_components::(); - entities.init_components::(); - entities.init_components::(); - entities.init_components::(); - entities.init_components::(); - entities.init_components::(); - entities.init_components::(); - entities.init_components::(); - entities.init_components::(); - entities.init_components::(); - entities.init_components::(); - entities.init_components::(); - entities.init_components::(); - entities.init_components::(); - entities.init_components::(); - entities.init_components::(); - entities.init_components::(); - entities.init_components::(); - entities.init_components::(); - entities.init_components::(); - entities.remove_all_entities(); + entities.init_components::(); + entities.init_components::(); + entities.init_components::(); + entities.init_components::(); + entities.init_components::(); + entities.init_components::(); + entities.init_components::(); + entities.init_components::(); + entities.init_components::(); + entities.init_components::(); + entities.init_components::(); + entities.init_components::(); + entities.init_components::(); + entities.init_components::(); + entities.init_components::(); + entities.init_components::(); + entities.init_components::(); + entities.init_components::(); + entities.init_components::(); + entities.init_components::(); + entities.init_components::(); + entities.init_components::(); + entities.init_components::(); + entities.init_components::(); + entities.init_components::(); + entities.init_components::(); + entities.init_components::(); + entities.init_components::(); + entities.init_components::(); + entities.init_components::(); + entities.init_components::(); + entities.init_components::(); + entities.init_components::(); + entities.init_components::(); + entities.init_components::(); + entities.init_components::(); + entities.init_components::(); + entities.init_components::(); + entities.remove_all_entities(); } /////////////////////////////////////////////////////////////////////////////////////////////////// pub fn new_world_entity(context: &mut Core, min_spawn_time: f32, max_spawn_time: f32, max_slimes: usize) -> EntityId { - let id = context.entities.new_entity(); + let id = context.entities.new_entity(); - context.entities.add_component(id, World); + context.entities.add_component(id, World); - let mut spawn_timer = SpawnTimer::new(min_spawn_time, max_spawn_time, max_slimes); - spawn_timer.reset_timer(); - context.entities.add_component(id, spawn_timer); + let mut spawn_timer = SpawnTimer::new(min_spawn_time, max_spawn_time, max_slimes); + spawn_timer.reset_timer(); + context.entities.add_component(id, spawn_timer); - id + id } pub fn new_camera_entity(context: &mut Core, x: i32, y: i32) -> EntityId { - let id = context.entities.new_entity(); - context.entities.add_component(id, Camera { x, y }); - id + let id = context.entities.new_entity(); + context.entities.add_component(id, Camera { x, y }); + id } pub fn new_slime_entity(context: &mut Core, x: i32, y: i32, direction: Direction, color: SlimeColor) -> EntityId { - let id = context.entities.new_entity(); + let id = context.entities.new_entity(); - let (atlas, chance_to_move, movement_speed, min_walk_time, max_walk_time, min_walk_cooldown, max_walk_cooldown, life, hit_color) = match color { - SlimeColor::Green => (context.green_slime.clone(), 10, 8.0, 0.5, 2.0, 0.5, 5.0, 1, 11), - SlimeColor::Blue => (context.blue_slime.clone(), 40, 12.0, 0.5, 2.0, 0.5, 3.0, 2, 13), - SlimeColor::Orange => (context.orange_slime.clone(), 90, 24.0, 0.5, 1.0, 0.5, 2.0, 3, 9), - }; + let (atlas, chance_to_move, movement_speed, min_walk_time, max_walk_time, min_walk_cooldown, max_walk_cooldown, life, hit_color) = match color { + SlimeColor::Green => (context.green_slime.clone(), 10, 8.0, 0.5, 2.0, 0.5, 5.0, 1, 11), + SlimeColor::Blue => (context.blue_slime.clone(), 40, 12.0, 0.5, 2.0, 0.5, 3.0, 2, 13), + SlimeColor::Orange => (context.orange_slime.clone(), 90, 24.0, 0.5, 1.0, 0.5, 2.0, 3, 9), + }; - let activity = EntityActivity::Idle; - let animate_by_activity = AnimateByActivity(context.slime_activity_states.clone()); - let animation = AnimationInstance::from(animate_by_activity.0.get(&activity).unwrap().clone()); + let activity = EntityActivity::Idle; + let animate_by_activity = AnimateByActivity(context.slime_activity_states.clone()); + let animation = AnimationInstance::from(animate_by_activity.0.get(&activity).unwrap().clone()); - context.entities.add_component(id, Slime); - context.entities.add_component(id, Position(Vector2::new(x as f32, y as f32))); - context.entities.add_component(id, Velocity(Vector2::ZERO)); - context.entities.add_component(id, Forces::new()); - context.entities.add_component(id, Bounds { width: 16, height: 16, radius: 8 }); - context.entities.add_component(id, FacingDirection(direction)); - context.entities.add_component(id, Sprite { atlas, index: 0 }); - context.entities.add_component(id, Activity(activity)); - context.entities.add_component(id, animate_by_activity); - context.entities.add_component(id, animation); - context.entities.add_component(id, RandomlyWalksAround::new(min_walk_time, max_walk_time, chance_to_move, min_walk_cooldown, max_walk_cooldown)); - context.entities.add_component(id, MovementSpeed(movement_speed)); - context.entities.add_component(id, Pusher::new()); - context.entities.add_component(id, Pushable); - context.entities.add_component(id, Attackable); - context.entities.add_component(id, Life(life)); - context.entities.add_component(id, HitParticleColor(hit_color)); + context.entities.add_component(id, Slime); + context.entities.add_component(id, Position(Vector2::new(x as f32, y as f32))); + context.entities.add_component(id, Velocity(Vector2::ZERO)); + context.entities.add_component(id, Forces::new()); + context.entities.add_component(id, Bounds { width: 16, height: 16, radius: 8 }); + context.entities.add_component(id, FacingDirection(direction)); + context.entities.add_component(id, Sprite { atlas, index: 0 }); + context.entities.add_component(id, Activity(activity)); + context.entities.add_component(id, animate_by_activity); + context.entities.add_component(id, animation); + context.entities.add_component(id, RandomlyWalksAround::new(min_walk_time, max_walk_time, chance_to_move, min_walk_cooldown, max_walk_cooldown)); + context.entities.add_component(id, MovementSpeed(movement_speed)); + context.entities.add_component(id, Pusher::new()); + context.entities.add_component(id, Pushable); + context.entities.add_component(id, Attackable); + context.entities.add_component(id, Life(life)); + context.entities.add_component(id, HitParticleColor(hit_color)); - id + id } pub fn spawn_slime_randomly(context: &mut Core) -> EntityId { - let (x, y) = context.tilemap.get_random_spawnable_coordinates(); - let id = new_slime_entity(context, x * TILE_WIDTH as i32, y * TILE_HEIGHT as i32, Direction::new_random(), SlimeColor::new_random()); - spawn_poof_cloud(context, x * TILE_WIDTH as i32, y * TILE_HEIGHT as i32, 4, 8); - id + let (x, y) = context.tilemap.get_random_spawnable_coordinates(); + let id = new_slime_entity(context, x * TILE_WIDTH as i32, y * TILE_HEIGHT as i32, Direction::new_random(), SlimeColor::new_random()); + spawn_poof_cloud(context, x * TILE_WIDTH as i32, y * TILE_HEIGHT as i32, 4, 8); + id } pub fn new_player_entity(context: &mut Core, x: i32, y: i32, direction: Direction) -> EntityId { - let id = context.entities.new_entity(); + let id = context.entities.new_entity(); - let (atlas, weapon_offsets) = if rnd_value(0, 1) == 0 { - ( - context.hero_female.clone(), - [ - Vector2::new(-3.0, 13.0), - Vector2::new(-14.0, 2.0), - Vector2::new(14.0, 2.0), - Vector2::new(3.0, -11.0) - ] - ) - } else { - ( - context.hero_male.clone(), - [ - Vector2::new(-3.0, 13.0), - Vector2::new(-13.0, 2.0), - Vector2::new(13.0, 2.0), - Vector2::new(3.0, -11.0) - ] - ) - }; + let (atlas, weapon_offsets) = if rnd_value(0, 1) == 0 { + ( + context.hero_female.clone(), + [ + Vector2::new(-3.0, 13.0), + Vector2::new(-14.0, 2.0), + Vector2::new(14.0, 2.0), + Vector2::new(3.0, -11.0) + ] + ) + } else { + ( + context.hero_male.clone(), + [ + Vector2::new(-3.0, 13.0), + Vector2::new(-13.0, 2.0), + Vector2::new(13.0, 2.0), + Vector2::new(3.0, -11.0) + ] + ) + }; - let activity = EntityActivity::Idle; - let animate_by_activity = AnimateByActivity(context.hero_activity_states.clone()); - let animation = AnimationInstance::from(animate_by_activity.0.get(&activity).unwrap().clone()); + let activity = EntityActivity::Idle; + let animate_by_activity = AnimateByActivity(context.hero_activity_states.clone()); + let animation = AnimationInstance::from(animate_by_activity.0.get(&activity).unwrap().clone()); - let weapon = Weapon { - atlas: context.sword.clone(), - base_index: 0, - offsets: weapon_offsets, - damage: 1, - radius_of_effect: 8, - }; + let weapon = Weapon { + atlas: context.sword.clone(), + base_index: 0, + offsets: weapon_offsets, + damage: 1, + radius_of_effect: 8, + }; - context.entities.add_component(id, Player); - context.entities.add_component(id, Position(Vector2::new(x as f32, y as f32))); - context.entities.add_component(id, Velocity(Vector2::ZERO)); - context.entities.add_component(id, Forces::new()); - context.entities.add_component(id, Bounds { width: 14, height: 14, radius: 8 }); - context.entities.add_component(id, FacingDirection(direction)); - context.entities.add_component(id, Sprite { atlas, index: 0 }); - context.entities.add_component(id, Activity(activity)); - context.entities.add_component(id, animate_by_activity); - context.entities.add_component(id, animation); - context.entities.add_component(id, MovementSpeed(32.0)); - context.entities.add_component(id, Pusher::new()); - context.entities.add_component(id, Pushable); - context.entities.add_component(id, weapon); - context.entities.add_component(id, Pickuper); + context.entities.add_component(id, Player); + context.entities.add_component(id, Position(Vector2::new(x as f32, y as f32))); + context.entities.add_component(id, Velocity(Vector2::ZERO)); + context.entities.add_component(id, Forces::new()); + context.entities.add_component(id, Bounds { width: 14, height: 14, radius: 8 }); + context.entities.add_component(id, FacingDirection(direction)); + context.entities.add_component(id, Sprite { atlas, index: 0 }); + context.entities.add_component(id, Activity(activity)); + context.entities.add_component(id, animate_by_activity); + context.entities.add_component(id, animation); + context.entities.add_component(id, MovementSpeed(32.0)); + context.entities.add_component(id, Pusher::new()); + context.entities.add_component(id, Pushable); + context.entities.add_component(id, weapon); + context.entities.add_component(id, Pickuper); - id + id } pub fn spawn_player_randomly(context: &mut Core) -> EntityId { - let (x, y) = context.tilemap.get_random_spawnable_coordinates(); - new_player_entity(context, x * TILE_WIDTH as i32, y * TILE_HEIGHT as i32, Direction::South) + let (x, y) = context.tilemap.get_random_spawnable_coordinates(); + new_player_entity(context, x * TILE_WIDTH as i32, y * TILE_HEIGHT as i32, Direction::South) } fn new_animation_effect(context: &mut Core, x: i32, y: i32, animation_def: Rc, delay_scaling_factor: Option) -> EntityId { - let id = context.entities.new_entity(); - context.entities.add_component(id, Particle); - context.entities.add_component(id, Position(Vector2::new(x as f32, y as f32))); - context.entities.add_component(id, Sprite { atlas: context.particles.clone(), index: 0 }); - context.entities.add_component(id, KillWhenAnimationFinishes); - context.entities.add_component(id, IgnoresCollision); - context.entities.add_component(id, IgnoresFriction); + let id = context.entities.new_entity(); + context.entities.add_component(id, Particle); + context.entities.add_component(id, Position(Vector2::new(x as f32, y as f32))); + context.entities.add_component(id, Sprite { atlas: context.particles.clone(), index: 0 }); + context.entities.add_component(id, KillWhenAnimationFinishes); + context.entities.add_component(id, IgnoresCollision); + context.entities.add_component(id, IgnoresFriction); - let mut animation = AnimationInstance::from(animation_def); - if let Some(delay_scaling_factor) = delay_scaling_factor { - animation.delay_override = Some(animation.def.delay * delay_scaling_factor); - } - context.entities.add_component(id, animation); + let mut animation = AnimationInstance::from(animation_def); + if let Some(delay_scaling_factor) = delay_scaling_factor { + animation.delay_override = Some(animation.def.delay * delay_scaling_factor); + } + context.entities.add_component(id, animation); - id + id } pub fn new_poof_animation(context: &mut Core, x: i32, y: i32, variant: usize, delay_scaling_factor: Option) -> EntityId { - let def = match variant { - 0 => context.poof1_animation_def.clone(), - 1 => context.poof2_animation_def.clone(), - _ => panic!("unknown poof animation variant") - }; - new_animation_effect(context, x, y, def, delay_scaling_factor) + let def = match variant { + 0 => context.poof1_animation_def.clone(), + 1 => context.poof2_animation_def.clone(), + _ => panic!("unknown poof animation variant") + }; + new_animation_effect(context, x, y, def, delay_scaling_factor) } pub fn new_sparkles_animation(context: &mut Core, x: i32, y: i32, delay_scaling_factor: Option) -> EntityId { - new_animation_effect(context, x, y, context.sparkles_animation_def.clone(), delay_scaling_factor) + new_animation_effect(context, x, y, context.sparkles_animation_def.clone(), delay_scaling_factor) } pub fn new_pixel_particle(context: &mut Core, x: i32, y: i32, velocity: Vector2, lifetime: f32, color: u8) -> EntityId { - let id = context.entities.new_entity(); - context.entities.add_component(id, Particle); - context.entities.add_component(id, Position(Vector2::new(x as f32, y as f32))); - context.entities.add_component(id, Velocity(velocity)); - context.entities.add_component(id, Pixel(color)); - context.entities.add_component(id, LifeTime(lifetime)); - context.entities.add_component(id, IgnoresCollision); - context.entities.add_component(id, IgnoresFriction); - id + let id = context.entities.new_entity(); + context.entities.add_component(id, Particle); + context.entities.add_component(id, Position(Vector2::new(x as f32, y as f32))); + context.entities.add_component(id, Velocity(velocity)); + context.entities.add_component(id, Pixel(color)); + context.entities.add_component(id, LifeTime(lifetime)); + context.entities.add_component(id, IgnoresCollision); + context.entities.add_component(id, IgnoresFriction); + id } pub fn spawn_pixel_cloud(context: &mut Core, x: i32, y: i32, count: usize, speed: f32, lifetime: f32, color: u8) { - let mut angle = 0.0; - for i in 0..count { - angle += RADIANS_360 / count as f32; - let velocity = Vector2::from_angle(angle) * speed; - new_pixel_particle(context, x, y, velocity, lifetime, color); - } + let mut angle = 0.0; + for i in 0..count { + angle += RADIANS_360 / count as f32; + let velocity = Vector2::from_angle(angle) * speed; + new_pixel_particle(context, x, y, velocity, lifetime, color); + } } pub fn spawn_poof_cloud(context: &mut Core, x: i32, y: i32, count: usize, radius: i32) { - for _ in 0..count { - let x = x + rnd_value(-radius, radius); - let y = y + rnd_value(-radius, radius); - new_poof_animation(context, x, y, 0, match rnd_value(0, 5) { - 0 => Some(0.25), - 1 => Some(0.5), - 2 => Some(0.75), - 3 => Some(1.0), - 4 => Some(1.25), - 5 => Some(1.5), - _ => None, - }); - } + for _ in 0..count { + let x = x + rnd_value(-radius, radius); + let y = y + rnd_value(-radius, radius); + new_poof_animation(context, x, y, 0, match rnd_value(0, 5) { + 0 => Some(0.25), + 1 => Some(0.5), + 2 => Some(0.75), + 3 => Some(1.0), + 4 => Some(1.25), + 5 => Some(1.5), + _ => None, + }); + } } pub fn new_weapon_attachment_entity(context: &mut Core, attached_to: EntityId) -> Option { - let sprite; - let sprite_by_direction; - let offset_by_direction; + let sprite; + let sprite_by_direction; + let offset_by_direction; - let weapons = context.entities.components::(); - if let Some(weapon) = weapons.get(&attached_to) { - sprite = Sprite { atlas: weapon.atlas.clone(), index: 0 }; - sprite_by_direction = SpriteIndexByDirection { base_index: weapon.base_index }; - offset_by_direction = AttachmentOffsetByDirection { offsets: weapon.offsets }; - } else { - // if the entity has no weapon "equipped" then they cannot attack! - return None; - } - drop(weapons); + let weapons = context.entities.components::(); + if let Some(weapon) = weapons.get(&attached_to) { + sprite = Sprite { atlas: weapon.atlas.clone(), index: 0 }; + sprite_by_direction = SpriteIndexByDirection { base_index: weapon.base_index }; + offset_by_direction = AttachmentOffsetByDirection { offsets: weapon.offsets }; + } else { + // if the entity has no weapon "equipped" then they cannot attack! + return None; + } + drop(weapons); - let id = context.entities.new_entity(); + let id = context.entities.new_entity(); - // note: no point in setting up initial position/direction, as attachment entities get these - // properties automatically applied from their parent each frame + // note: no point in setting up initial position/direction, as attachment entities get these + // properties automatically applied from their parent each frame - context.entities.add_component(id, AttachedTo(attached_to)); - context.entities.add_component(id, Position(Vector2::ZERO)); - context.entities.add_component(id, FacingDirection(Direction::South)); - context.entities.add_component(id, sprite_by_direction); - context.entities.add_component(id, sprite); - context.entities.add_component(id, offset_by_direction); + context.entities.add_component(id, AttachedTo(attached_to)); + context.entities.add_component(id, Position(Vector2::ZERO)); + context.entities.add_component(id, FacingDirection(Direction::South)); + context.entities.add_component(id, sprite_by_direction); + context.entities.add_component(id, sprite); + context.entities.add_component(id, offset_by_direction); - context.entities.add_component(attached_to, Attachment(id)); + context.entities.add_component(attached_to, Attachment(id)); - Some(id) + Some(id) } pub fn new_pickupable_entity(context: &mut Core, x: i32, y: i32, force: Force, kind: PickupType) -> EntityId { - let id = context.entities.new_entity(); + let id = context.entities.new_entity(); - let mut forces = Forces::new(); - forces.forces.push(force); + let mut forces = Forces::new(); + forces.forces.push(force); - let sprite_index = match kind { - PickupType::BlueGem => 0, - PickupType::GreenGem => 1, - PickupType::OrangeGem => 2, - PickupType::Coin => 3, - }; + let sprite_index = match kind { + PickupType::BlueGem => 0, + PickupType::GreenGem => 1, + PickupType::OrangeGem => 2, + PickupType::Coin => 3, + }; - context.entities.add_component(id, Pickupable { kind, pre_timer: PICKUP_PRE_TIMER }); - context.entities.add_component(id, Position(Vector2::new(x as f32, y as f32))); - context.entities.add_component(id, Velocity(Vector2::ZERO)); - context.entities.add_component(id, forces); - context.entities.add_component(id, Sprite { atlas: context.items.clone(), index: sprite_index }); - context.entities.add_component(id, Bounds { width: 16, height: 16, radius: 8 }); - context.entities.add_component(id, LifeTime(10.0)); - context.entities.add_component(id, TimedFlicker::new_with_pre_timer(10.0, 7.0, FlickerMethod::OnOff)); + context.entities.add_component(id, Pickupable { kind, pre_timer: PICKUP_PRE_TIMER }); + context.entities.add_component(id, Position(Vector2::new(x as f32, y as f32))); + context.entities.add_component(id, Velocity(Vector2::ZERO)); + context.entities.add_component(id, forces); + context.entities.add_component(id, Sprite { atlas: context.items.clone(), index: sprite_index }); + context.entities.add_component(id, Bounds { width: 16, height: 16, radius: 8 }); + context.entities.add_component(id, LifeTime(10.0)); + context.entities.add_component(id, TimedFlicker::new_with_pre_timer(10.0, 7.0, FlickerMethod::OnOff)); - id + id } pub fn spawn_pickups_from_entity(context: &mut Core, entity: EntityId) { - let positions = context.entities.components::(); - let position = positions.get(&entity).unwrap().0; - drop(positions); + let positions = context.entities.components::(); + let position = positions.get(&entity).unwrap().0; + drop(positions); - let num_pickups = rnd_value(0, 5); - for _ in 0..num_pickups { - let angle = (rnd_value(0, 359) as f32).to_radians(); - let force_strength = rnd_value(0.5, 5.0); - let force = Force { - force: Vector2::from_angle(angle) * force_strength, - dissipation_factor: 0.5, - }; - let kind = PickupType::new_random(); - new_pickupable_entity(context, position.x as i32, position.y as i32, force, kind); - } + let num_pickups = rnd_value(0, 5); + for _ in 0..num_pickups { + let angle = (rnd_value(0, 359) as f32).to_radians(); + let force_strength = rnd_value(0.5, 5.0); + let force = Force { + force: Vector2::from_angle(angle) * force_strength, + dissipation_factor: 0.5, + }; + let kind = PickupType::new_random(); + new_pickupable_entity(context, position.x as i32, position.y as i32, force, kind); + } } diff --git a/examples/slimed/src/entities/systems.rs b/examples/slimed/src/entities/systems.rs index bd004df..fed8c36 100644 --- a/examples/slimed/src/entities/systems.rs +++ b/examples/slimed/src/entities/systems.rs @@ -7,738 +7,738 @@ use crate::entities::*; use crate::tilemap::*; pub fn remove_entity(entities: &mut Entities, entity: EntityId) { - remove_entity_attachment(entities, entity); - entities.remove_entity(entity); + remove_entity_attachment(entities, entity); + entities.remove_entity(entity); } pub fn remove_entity_attachment(entities: &mut Entities, entity: EntityId) { - let attachments = entities.components::(); - if let Some(attachment) = attachments.get(&entity) { - let attached_entity_id = attachment.0; - drop(attachments); - entities.remove_entity(attached_entity_id); - } + let attachments = entities.components::(); + if let Some(attachment) = attachments.get(&entity) { + let attached_entity_id = attachment.0; + drop(attachments); + entities.remove_entity(attached_entity_id); + } } pub fn move_entity_forward(context: &mut Core, entity: EntityId) { - let mut velocities = context.entities.components_mut::(); - let facing_directions = context.entities.components::(); - let movement_speeds = context.entities.components::(); + let mut velocities = context.entities.components_mut::(); + let facing_directions = context.entities.components::(); + let movement_speeds = context.entities.components::(); - let velocity = velocities.get_mut(&entity).unwrap(); - let facing_direction = facing_directions.get(&entity).unwrap(); - let movement_speed = movement_speeds.get(&entity).unwrap(); + let velocity = velocities.get_mut(&entity).unwrap(); + let facing_direction = facing_directions.get(&entity).unwrap(); + let movement_speed = movement_speeds.get(&entity).unwrap(); - let movement = match facing_direction.0 { - Direction::North => Vector2::UP * movement_speed.0, - Direction::East => Vector2::RIGHT * movement_speed.0, - Direction::West => Vector2::LEFT * movement_speed.0, - Direction::South => Vector2::DOWN * movement_speed.0 - }; + let movement = match facing_direction.0 { + Direction::North => Vector2::UP * movement_speed.0, + Direction::East => Vector2::RIGHT * movement_speed.0, + Direction::West => Vector2::LEFT * movement_speed.0, + Direction::South => Vector2::DOWN * movement_speed.0 + }; - velocity.0 += movement; + velocity.0 += movement; } pub fn turn_and_move_entity(context: &mut Core, entity: EntityId, direction: Direction) { - // can this entity currently move at all? - let activities = context.entities.components::(); - if let Some(activity) = activities.get(&entity) { - if activity.0 != EntityActivity::Idle && activity.0 != EntityActivity::Walking { - return; - } - } - drop(activities); + // can this entity currently move at all? + let activities = context.entities.components::(); + if let Some(activity) = activities.get(&entity) { + if activity.0 != EntityActivity::Idle && activity.0 != EntityActivity::Walking { + return; + } + } + drop(activities); - // make the entity face in the direction specified - let mut facing_directions = context.entities.components_mut::(); - let facing_direction = facing_directions.get_mut(&entity).unwrap(); - facing_direction.0 = direction; - drop(facing_directions); + // make the entity face in the direction specified + let mut facing_directions = context.entities.components_mut::(); + let facing_direction = facing_directions.get_mut(&entity).unwrap(); + facing_direction.0 = direction; + drop(facing_directions); - move_entity_forward(context, entity); + move_entity_forward(context, entity); } fn move_entity_with_collision(position: &mut Position, bounds: &Bounds, velocity: Option<&Velocity>, forces: Option<&Forces>, map: &TileMap, delta: f32) -> bool { - const NUM_STEPS: usize = 2; - const STEP_SCALE: f32 = 1.0 / NUM_STEPS as f32; + const NUM_STEPS: usize = 2; + const STEP_SCALE: f32 = 1.0 / NUM_STEPS as f32; - let mut collided = false; + let mut collided = false; - // apply entity velocity + force (if any/either) and exit early with no collision if this entity - // has no movement ... no need to check collisions in such a case - let mut step_velocity = Vector2::ZERO; - if let Some(velocity) = velocity { - step_velocity += velocity.0 * delta; - } - if let Some(forces) = forces { - step_velocity += forces.current_force(); - } - if step_velocity.nearly_equal(Vector2::ZERO, 0.00001) { - return collided; - } + // apply entity velocity + force (if any/either) and exit early with no collision if this entity + // has no movement ... no need to check collisions in such a case + let mut step_velocity = Vector2::ZERO; + if let Some(velocity) = velocity { + step_velocity += velocity.0 * delta; + } + if let Some(forces) = forces { + step_velocity += forces.current_force(); + } + if step_velocity.nearly_equal(Vector2::ZERO, 0.00001) { + return collided; + } - // entity is actually moving, so check collisions and move accordingly - step_velocity *= STEP_SCALE; - for _ in 0..NUM_STEPS { - let old_position = position.0; + // entity is actually moving, so check collisions and move accordingly + step_velocity *= STEP_SCALE; + for _ in 0..NUM_STEPS { + let old_position = position.0; - position.0.x += step_velocity.x; - if map.is_colliding(&Rect::new(position.0.x as i32, position.0.y as i32, bounds.width, bounds.height)) { - collided = true; - position.0.x = old_position.x; - } + position.0.x += step_velocity.x; + if map.is_colliding(&Rect::new(position.0.x as i32, position.0.y as i32, bounds.width, bounds.height)) { + collided = true; + position.0.x = old_position.x; + } - position.0.y += step_velocity.y; - if map.is_colliding(&Rect::new(position.0.x as i32, position.0.y as i32, bounds.width, bounds.height)) { - collided = true; - position.0.y = old_position.y; - } - } + position.0.y += step_velocity.y; + if map.is_colliding(&Rect::new(position.0.x as i32, position.0.y as i32, bounds.width, bounds.height)) { + collided = true; + position.0.y = old_position.y; + } + } - collided + collided } pub fn set_entity_activity(entities: &mut Entities, entity: EntityId, new_activity: EntityActivity) { - let mut activities = entities.components_mut::(); - let mut activity = activities.get_mut(&entity).unwrap(); + let mut activities = entities.components_mut::(); + let mut activity = activities.get_mut(&entity).unwrap(); - // only change the activity, and more importantly, the animation if we are actually applying - // an actual activity change from what it was before - if activity.0 != new_activity { - activity.0 = new_activity; + // only change the activity, and more importantly, the animation if we are actually applying + // an actual activity change from what it was before + if activity.0 != new_activity { + activity.0 = new_activity; - let animate_by_activitys = entities.components::(); - if let Some(animate_by_activity) = animate_by_activitys.get(&entity) { - if let Some(new_animation_def) = animate_by_activity.0.get(&new_activity) { - let mut animations = entities.components_mut::(); - let animation = animations.get_mut(&entity).unwrap(); - animation.change_to(new_animation_def.clone()); - } - } - } + let animate_by_activitys = entities.components::(); + if let Some(animate_by_activity) = animate_by_activitys.get(&entity) { + if let Some(new_animation_def) = animate_by_activity.0.get(&new_activity) { + let mut animations = entities.components_mut::(); + let animation = animations.get_mut(&entity).unwrap(); + animation.change_to(new_animation_def.clone()); + } + } + } } pub fn kill_entity(context: &mut Core, entity: EntityId) { - context.entities.add_component(entity, LifeTime(5.0)); - context.entities.add_component(entity, TimedFlicker::new_with_pre_timer(5.0, 4.0, FlickerMethod::OnOff)); - context.entities.remove_component::(entity); - context.entities.remove_component::(entity); - context.entities.remove_component::(entity); - context.entities.remove_component::(entity); - context.entities.remove_component::(entity); - set_entity_activity(&mut context.entities, entity, EntityActivity::Dead); - spawn_pickups_from_entity(context, entity); + context.entities.add_component(entity, LifeTime(5.0)); + context.entities.add_component(entity, TimedFlicker::new_with_pre_timer(5.0, 4.0, FlickerMethod::OnOff)); + context.entities.remove_component::(entity); + context.entities.remove_component::(entity); + context.entities.remove_component::(entity); + context.entities.remove_component::(entity); + context.entities.remove_component::(entity); + set_entity_activity(&mut context.entities, entity, EntityActivity::Dead); + spawn_pickups_from_entity(context, entity); } pub fn apply_damage_at(context: &mut Core, area: Circle, damage: i32, source: EntityId) { - if let Some(attackables) = context.entities.components::() { - let positions = context.entities.components::(); - let bounds = context.entities.components::(); + if let Some(attackables) = context.entities.components::() { + let positions = context.entities.components::(); + let bounds = context.entities.components::(); - //let source_position = Vector2::new(area.x as f32, area.y as f32); - let source_position = positions.get(&source).unwrap(); + //let source_position = Vector2::new(area.x as f32, area.y as f32); + let source_position = positions.get(&source).unwrap(); - for (entity, _) in attackables.iter() { - // entity cannot (currently) attack itself ... - if *entity == source { - continue; - } + for (entity, _) in attackables.iter() { + // entity cannot (currently) attack itself ... + if *entity == source { + continue; + } - let position = positions.get(entity).unwrap(); - let bound = bounds.get(entity).unwrap(); + let position = positions.get(entity).unwrap(); + let bound = bounds.get(entity).unwrap(); - let circle = Circle::new( - position.0.x as i32 + bound.width as i32 / 2, - position.0.y as i32 + bound.height as i32 / 2, - bound.radius - ); - if area.overlaps(&circle) { - context.event_publisher.queue(Event::Hit(*entity, source, damage, source_position.0)); - } - } - } + let circle = Circle::new( + position.0.x as i32 + bound.width as i32 / 2, + position.0.y as i32 + bound.height as i32 / 2, + bound.radius, + ); + if area.overlaps(&circle) { + context.event_publisher.queue(Event::Hit(*entity, source, damage, source_position.0)); + } + } + } } pub fn get_attack_area_of_effect(context: &mut Core, attacker: EntityId) -> Option<(Circle, i32)> { - let positions = context.entities.components::(); - let facing_directions = context.entities.components::(); - let bounds = context.entities.components::(); - let weapons = context.entities.components::(); + let positions = context.entities.components::(); + let facing_directions = context.entities.components::(); + let bounds = context.entities.components::(); + let weapons = context.entities.components::(); - let position = positions.get(&attacker).unwrap(); - let bound = bounds.get(&attacker).unwrap(); - if let Some(weapon) = weapons.get(&attacker) { - if let Some(facing_direction) = facing_directions.get(&attacker) { - let center_point = position.0 + weapon.offsets[facing_direction.0 as usize]; - return Some(( - Circle::new( - center_point.x as i32 + 8, - center_point.y as i32 + 8, - weapon.radius_of_effect, - ), - weapon.damage - )); - } else { - return Some(( - Circle::new( - position.0.x as i32 + bound.width as i32 / 2, - position.0.y as i32 + bound.height as i32 / 2, - weapon.radius_of_effect, - ), - weapon.damage - )); - } - } + let position = positions.get(&attacker).unwrap(); + let bound = bounds.get(&attacker).unwrap(); + if let Some(weapon) = weapons.get(&attacker) { + if let Some(facing_direction) = facing_directions.get(&attacker) { + let center_point = position.0 + weapon.offsets[facing_direction.0 as usize]; + return Some(( + Circle::new( + center_point.x as i32 + 8, + center_point.y as i32 + 8, + weapon.radius_of_effect, + ), + weapon.damage + )); + } else { + return Some(( + Circle::new( + position.0.x as i32 + bound.width as i32 / 2, + position.0.y as i32 + bound.height as i32 / 2, + weapon.radius_of_effect, + ), + weapon.damage + )); + } + } - None + None } pub fn attack(context: &mut Core, entity: EntityId) { - let activities = context.entities.components::(); - let activity = activities.get(&entity).unwrap(); + let activities = context.entities.components::(); + let activity = activities.get(&entity).unwrap(); - match activity.0 { - EntityActivity::Idle | EntityActivity::Walking => { - drop(activities); - // set attacking animation and "extend" the entity's weapon - set_entity_activity(&mut context.entities, entity, EntityActivity::Attacking); - if new_weapon_attachment_entity(context, entity).is_some() { - // if the entity's weapon was actually extended, figure out where it hits - // and who is being hit by it - if let Some((area_of_effect, damage)) = get_attack_area_of_effect(context, entity) { - apply_damage_at(context, area_of_effect, damage, entity); - } - } - } - _ => {} - } + match activity.0 { + EntityActivity::Idle | EntityActivity::Walking => { + drop(activities); + // set attacking animation and "extend" the entity's weapon + set_entity_activity(&mut context.entities, entity, EntityActivity::Attacking); + if new_weapon_attachment_entity(context, entity).is_some() { + // if the entity's weapon was actually extended, figure out where it hits + // and who is being hit by it + if let Some((area_of_effect, damage)) = get_attack_area_of_effect(context, entity) { + apply_damage_at(context, area_of_effect, damage, entity); + } + } + } + _ => {} + } } pub fn hit_entity(context: &mut Core, target: EntityId, source: EntityId, damage: i32, damage_position: Vector2) { - let position; - { - let positions = context.entities.components::(); - position = positions.get(&target).unwrap().0; + let position; + { + let positions = context.entities.components::(); + position = positions.get(&target).unwrap().0; - // apply knockback force to target being hit - let mut forces = context.entities.components_mut::(); - if let Some(force) = forces.get_mut(&target) { - let knockback_direction = (position - damage_position).normalize(); - force.add(knockback_direction * HIT_KNOCKBACK_STRENGTH, HIT_KNOCKBACK_DISSIPATION); - } + // apply knockback force to target being hit + let mut forces = context.entities.components_mut::(); + if let Some(force) = forces.get_mut(&target) { + let knockback_direction = (position - damage_position).normalize(); + force.add(knockback_direction * HIT_KNOCKBACK_STRENGTH, HIT_KNOCKBACK_DISSIPATION); + } - // subtract damage from entity life, and kill if necessary - let mut lifes = context.entities.components_mut::(); - if let Some(life) = lifes.get_mut(&target) { - life.0 -= damage; - if life.0 <= 0 { - context.event_publisher.queue(Event::Kill(target)); - } - } - } + // subtract damage from entity life, and kill if necessary + let mut lifes = context.entities.components_mut::(); + if let Some(life) = lifes.get_mut(&target) { + life.0 -= damage; + if life.0 <= 0 { + context.event_publisher.queue(Event::Kill(target)); + } + } + } - spawn_pixel_cloud(context, position.x as i32, position.y as i32, 8, 64.0, 0.15, 15); - context.entities.add_component(target, TimedFlicker::new(0.5, FlickerMethod::Color(4))); + spawn_pixel_cloud(context, position.x as i32, position.y as i32, 8, 64.0, 0.15, 15); + context.entities.add_component(target, TimedFlicker::new(0.5, FlickerMethod::Color(4))); } pub fn stop_attack(context: &mut Core, entity: EntityId) { - // after an entity's attack has finished, they go back to idle and we "sheath" their weapon - set_entity_activity(&mut context.entities, entity, EntityActivity::Idle); - remove_entity_attachment(&mut context.entities, entity); + // after an entity's attack has finished, they go back to idle and we "sheath" their weapon + set_entity_activity(&mut context.entities, entity, EntityActivity::Idle); + remove_entity_attachment(&mut context.entities, entity); } pub fn pickup(context: &mut Core, picked_up_by: EntityId, picked_up: EntityId) { - let kind; - let position; - { - let positions = context.entities.components::(); - position = positions.get(&picked_up).unwrap().0; + let kind; + let position; + { + let positions = context.entities.components::(); + position = positions.get(&picked_up).unwrap().0; - let pickupables = context.entities.components::(); - kind = pickupables.get(&picked_up).unwrap().kind; - } + let pickupables = context.entities.components::(); + kind = pickupables.get(&picked_up).unwrap().kind; + } - // TODO: tally up the kinds + // TODO: tally up the kinds - new_sparkles_animation(context, position.x as i32, position.y as i32, None); - remove_entity(&mut context.entities, picked_up); + new_sparkles_animation(context, position.x as i32, position.y as i32, None); + remove_entity(&mut context.entities, picked_up); } /////////////////////////////////////////////////////////////////////////////////////////////////// fn update_system_movement(context: &mut Core) { - let mut positions = context.entities.components_mut::().unwrap(); - let velocities = context.entities.components::(); - let forces = context.entities.components::(); - let bounds = context.entities.components::(); - let ignores_collision = context.entities.components::(); + let mut positions = context.entities.components_mut::().unwrap(); + let velocities = context.entities.components::(); + let forces = context.entities.components::(); + let bounds = context.entities.components::(); + let ignores_collision = context.entities.components::(); - for (entity, position) in positions.iter_mut() { - if ignores_collision.contains_key(entity) { - if let Some(velocity) = velocities.get(entity) { - position.0 += velocity.0 * context.delta; - } - } else { - let velocity = velocities.get(entity); - let force = forces.get(entity); + for (entity, position) in positions.iter_mut() { + if ignores_collision.contains_key(entity) { + if let Some(velocity) = velocities.get(entity) { + position.0 += velocity.0 * context.delta; + } + } else { + let velocity = velocities.get(entity); + let force = forces.get(entity); - if velocity.is_some() || force.is_some() { - move_entity_with_collision( - position, - bounds.get(entity).unwrap(), - velocity, - force, - &context.tilemap, - context.delta, - ); - } - } - } + if velocity.is_some() || force.is_some() { + move_entity_with_collision( + position, + bounds.get(entity).unwrap(), + velocity, + force, + &context.tilemap, + context.delta, + ); + } + } + } } fn update_system_friction(context: &mut Core) { - let mut velocities = context.entities.components_mut::().unwrap(); - let ignores_friction = context.entities.components::(); + let mut velocities = context.entities.components_mut::().unwrap(); + let ignores_friction = context.entities.components::(); - for (entity, velocity) in velocities.iter_mut() { - if !ignores_friction.contains_key(entity) { - velocity.0 *= FRICTION; - if velocity.0.almost_zero(0.001) { - velocity.0 = Vector2::ZERO; - } - } - } + for (entity, velocity) in velocities.iter_mut() { + if !ignores_friction.contains_key(entity) { + velocity.0 *= FRICTION; + if velocity.0.almost_zero(0.001) { + velocity.0 = Vector2::ZERO; + } + } + } } fn update_system_force_decay(context: &mut Core) { - let mut forces = context.entities.components_mut::().unwrap(); - for (_, force) in forces.iter_mut() { - force.decay(); - } + let mut forces = context.entities.components_mut::().unwrap(); + for (_, force) in forces.iter_mut() { + force.decay(); + } } fn update_system_pushing(context: &mut Core) { - let positions = context.entities.components::(); - let bounds = context.entities.components::(); - let mut forces = context.entities.components_mut::(); - let pushers = context.entities.components::().unwrap(); - let pushable = context.entities.components::().unwrap(); + let positions = context.entities.components::(); + let bounds = context.entities.components::(); + let mut forces = context.entities.components_mut::(); + let pushers = context.entities.components::().unwrap(); + let pushable = context.entities.components::().unwrap(); - // TODO: this is slow + // TODO: this is slow - for (pusher_entity, pusher) in pushers.iter() { - let pusher_position = positions.get(pusher_entity).unwrap(); - let pusher_bounds = bounds.get(pusher_entity).unwrap(); - let pusher_circle = Circle::new(pusher_position.0.x as i32, pusher_position.0.y as i32, pusher_bounds.radius); + for (pusher_entity, pusher) in pushers.iter() { + let pusher_position = positions.get(pusher_entity).unwrap(); + let pusher_bounds = bounds.get(pusher_entity).unwrap(); + let pusher_circle = Circle::new(pusher_position.0.x as i32, pusher_position.0.y as i32, pusher_bounds.radius); - for (pushable_entity, pushable) in pushable.iter() { - // don't push ourself ... - if *pushable_entity == *pusher_entity { - continue; - } + for (pushable_entity, pushable) in pushable.iter() { + // don't push ourself ... + if *pushable_entity == *pusher_entity { + continue; + } - let pushable_position = positions.get(pushable_entity).unwrap(); - let pushable_bounds = bounds.get(pushable_entity).unwrap(); - let pushable_circle = Circle::new(pushable_position.0.x as i32, pushable_position.0.y as i32, pushable_bounds.radius); + let pushable_position = positions.get(pushable_entity).unwrap(); + let pushable_bounds = bounds.get(pushable_entity).unwrap(); + let pushable_circle = Circle::new(pushable_position.0.x as i32, pushable_position.0.y as i32, pushable_bounds.radius); - if pusher_circle.overlaps(&pushable_circle) { - let push_direction = (pushable_position.0 - pusher_position.0).normalize(); + if pusher_circle.overlaps(&pushable_circle) { + let push_direction = (pushable_position.0 - pusher_position.0).normalize(); - let pushable_force = forces.get_mut(pushable_entity).unwrap(); - pushable_force.add(push_direction * pusher.strength, pusher.push_force_dissipation); - } - } - } + let pushable_force = forces.get_mut(pushable_entity).unwrap(); + pushable_force.add(push_direction * pusher.strength, pusher.push_force_dissipation); + } + } + } } fn update_system_lifetime(context: &mut Core) { - let mut lifetimes = context.entities.components_mut::().unwrap(); - for (entity, lifetime) in lifetimes.iter_mut() { - lifetime.0 -= context.delta; - if lifetime.0 < 0.0 { - context.event_publisher.queue(Event::Remove(*entity)); - } - } + let mut lifetimes = context.entities.components_mut::().unwrap(); + for (entity, lifetime) in lifetimes.iter_mut() { + lifetime.0 -= context.delta; + if lifetime.0 < 0.0 { + context.event_publisher.queue(Event::Remove(*entity)); + } + } } fn update_system_animation(context: &mut Core) { - let mut animations = context.entities.components_mut::().unwrap(); - let kill_when_animation_finishes = context.entities.components::(); + let mut animations = context.entities.components_mut::().unwrap(); + let kill_when_animation_finishes = context.entities.components::(); - for (entity, animation) in animations.iter_mut() { - if animation.complete { - continue; - } + for (entity, animation) in animations.iter_mut() { + if animation.complete { + continue; + } - animation.frame_timer += context.delta; + animation.frame_timer += context.delta; - let delay = if let Some(delay_override) = animation.delay_override { - delay_override - } else { - animation.def.delay - }; + let delay = if let Some(delay_override) = animation.delay_override { + delay_override + } else { + animation.def.delay + }; - if animation.frame_timer >= delay { - // move to the next frame in the current sequence - animation.frame_timer = 0.0; - if animation.frame_index == (animation.def.frames.len() - 1) { - // we're at the last frame in the current sequence - if !animation.def.loops { - animation.complete = true; + if animation.frame_timer >= delay { + // move to the next frame in the current sequence + animation.frame_timer = 0.0; + if animation.frame_index == (animation.def.frames.len() - 1) { + // we're at the last frame in the current sequence + if !animation.def.loops { + animation.complete = true; - context.event_publisher.queue(Event::AnimationFinished(*entity)); - if kill_when_animation_finishes.contains_key(entity) { - context.event_publisher.queue(Event::Remove(*entity)); - } - } else { - animation.frame_index = 0; - } - } else { - animation.frame_index += 1; - } - } - } + context.event_publisher.queue(Event::AnimationFinished(*entity)); + if kill_when_animation_finishes.contains_key(entity) { + context.event_publisher.queue(Event::Remove(*entity)); + } + } else { + animation.frame_index = 0; + } + } else { + animation.frame_index += 1; + } + } + } } fn update_system_set_sprite_index_from_animation(context: &mut Core) { - let animations = context.entities.components::().unwrap(); - let mut sprites = context.entities.components_mut::(); - let facing_directions = context.entities.components::(); + let animations = context.entities.components::().unwrap(); + let mut sprites = context.entities.components_mut::(); + let facing_directions = context.entities.components::(); - for (entity, animation) in animations.iter() { - if let Some(sprite) = sprites.get_mut(entity) { - // base animation sprite-sheet index for the current animation state - let mut index = animation.def.frames[animation.frame_index]; + for (entity, animation) in animations.iter() { + if let Some(sprite) = sprites.get_mut(entity) { + // base animation sprite-sheet index for the current animation state + let mut index = animation.def.frames[animation.frame_index]; - // add multi-direction offset if applicable - let multi_direction_offset = animation.def.multi_direction_offset; - let facing_direction = facing_directions.get(entity); - if multi_direction_offset.is_some() && facing_direction.is_some() { - index += multi_direction_offset.unwrap() * facing_direction.unwrap().0 as usize; - } + // add multi-direction offset if applicable + let multi_direction_offset = animation.def.multi_direction_offset; + let facing_direction = facing_directions.get(entity); + if multi_direction_offset.is_some() && facing_direction.is_some() { + index += multi_direction_offset.unwrap() * facing_direction.unwrap().0 as usize; + } - sprite.index = index; - } - } + sprite.index = index; + } + } } fn update_system_set_sprite_index_by_direction(context: &mut Core) { - let sprite_index_by_directions = context.entities.components::().unwrap(); - let mut sprites = context.entities.components_mut::(); - let facing_directions = context.entities.components::(); + let sprite_index_by_directions = context.entities.components::().unwrap(); + let mut sprites = context.entities.components_mut::(); + let facing_directions = context.entities.components::(); - for (entity, sprite_index_by_direction) in sprite_index_by_directions.iter() { - if let Some(sprite) = sprites.get_mut(entity) { - if let Some(facing_direction) = facing_directions.get(entity) { - sprite.index = sprite_index_by_direction.base_index + facing_direction.0 as usize; - } - } - } + for (entity, sprite_index_by_direction) in sprite_index_by_directions.iter() { + if let Some(sprite) = sprites.get_mut(entity) { + if let Some(facing_direction) = facing_directions.get(entity) { + sprite.index = sprite_index_by_direction.base_index + facing_direction.0 as usize; + } + } + } } fn update_system_walking_time(context: &mut Core) { - let mut walking_times = context.entities.components_mut::().unwrap(); - let activities = context.entities.components::(); + let mut walking_times = context.entities.components_mut::().unwrap(); + let activities = context.entities.components::(); - for (entity, walking_time) in walking_times.iter_mut() { - if let Some(activity) = activities.get(entity) { - // dead entities can't walk! - if activity.0 == EntityActivity::Dead { - continue; - } - } - if walking_time.0 > 0.0 { - walking_time.0 -= context.delta; - context.event_publisher.queue(Event::MoveForward(*entity)); - } - } + for (entity, walking_time) in walking_times.iter_mut() { + if let Some(activity) = activities.get(entity) { + // dead entities can't walk! + if activity.0 == EntityActivity::Dead { + continue; + } + } + if walking_time.0 > 0.0 { + walking_time.0 -= context.delta; + context.event_publisher.queue(Event::MoveForward(*entity)); + } + } - // remove walking time components whose timers have elapsed - walking_times.retain(|_, comp| comp.0 > 0.0); + // remove walking time components whose timers have elapsed + walking_times.retain(|_, comp| comp.0 > 0.0); } fn update_system_randomly_walk_around(context: &mut Core) { - let mut randomly_walk_arounds = context.entities.components_mut::().unwrap(); - let activities = context.entities.components::(); - let mut walking_times = context.entities.components_mut::().unwrap(); + let mut randomly_walk_arounds = context.entities.components_mut::().unwrap(); + let activities = context.entities.components::(); + let mut walking_times = context.entities.components_mut::().unwrap(); - for (entity, randomly_walk_around) in randomly_walk_arounds.iter_mut() { - if let Some(activity) = activities.get(entity) { - if activity.0 == EntityActivity::Idle { - if randomly_walk_around.cooldown_timer > 0.0 { - randomly_walk_around.cooldown_timer -= context.delta; - if randomly_walk_around.cooldown_timer < 0.0 { - randomly_walk_around.cooldown_timer = 0.0; - } - } else if randomly_walk_around.should_start_walking() { - randomly_walk_around.cooldown_timer = rnd_value(randomly_walk_around.min_cooldown, randomly_walk_around.max_cooldown); + for (entity, randomly_walk_around) in randomly_walk_arounds.iter_mut() { + if let Some(activity) = activities.get(entity) { + if activity.0 == EntityActivity::Idle { + if randomly_walk_around.cooldown_timer > 0.0 { + randomly_walk_around.cooldown_timer -= context.delta; + if randomly_walk_around.cooldown_timer < 0.0 { + randomly_walk_around.cooldown_timer = 0.0; + } + } else if randomly_walk_around.should_start_walking() { + randomly_walk_around.cooldown_timer = rnd_value(randomly_walk_around.min_cooldown, randomly_walk_around.max_cooldown); - let direction = Direction::new_random(); - let walk_time = rnd_value(randomly_walk_around.min_walk_time, randomly_walk_around.max_walk_time); + let direction = Direction::new_random(); + let walk_time = rnd_value(randomly_walk_around.min_walk_time, randomly_walk_around.max_walk_time); - walking_times.insert(*entity, WalkingTime(walk_time)); - context.event_publisher.queue(Event::TurnAndMove(*entity, direction)); - } - } - } - } + walking_times.insert(*entity, WalkingTime(walk_time)); + context.event_publisher.queue(Event::TurnAndMove(*entity, direction)); + } + } + } + } } fn update_system_current_entity_activity(context: &mut Core) { - let activities = context.entities.components::().unwrap(); - let velocities = context.entities.components::(); + let activities = context.entities.components::().unwrap(); + let velocities = context.entities.components::(); - for (entity, activity) in activities.iter() { - // try to detect current entity activity based on it's own movement speed - // (intentionally NOT checking force velocity!) - if let Some(velocity) = velocities.get(entity) { - match activity.0 { - EntityActivity::Idle => { - if velocity.0.length_squared() > 0.0 { - context.event_publisher.queue(Event::SetActivity(*entity, EntityActivity::Walking)); - } - } - EntityActivity::Walking => { - if velocity.0.almost_zero(0.001) { - context.event_publisher.queue(Event::SetActivity(*entity, EntityActivity::Idle)); - } - } - _ => {} - } - } - } + for (entity, activity) in activities.iter() { + // try to detect current entity activity based on it's own movement speed + // (intentionally NOT checking force velocity!) + if let Some(velocity) = velocities.get(entity) { + match activity.0 { + EntityActivity::Idle => { + if velocity.0.length_squared() > 0.0 { + context.event_publisher.queue(Event::SetActivity(*entity, EntityActivity::Walking)); + } + } + EntityActivity::Walking => { + if velocity.0.almost_zero(0.001) { + context.event_publisher.queue(Event::SetActivity(*entity, EntityActivity::Idle)); + } + } + _ => {} + } + } + } } fn update_system_randomly_spawn_slimes(context: &mut Core) { - if let Some((entity, _)) = context.entities.components::().single() { - let mut spawn_timers = context.entities.components_mut::(); - if let Some(spawn_timer) = spawn_timers.get_mut(entity) { - spawn_timer.timer -= context.delta; - if spawn_timer.timer <= 0.0 { - spawn_timer.reset_timer(); - let slime_count = context.entities.components::().len(); - if slime_count < spawn_timer.max_allowed { - context.event_publisher.queue(Event::SpawnSlimeRandomly); - } - } - } - } + if let Some((entity, _)) = context.entities.components::().single() { + let mut spawn_timers = context.entities.components_mut::(); + if let Some(spawn_timer) = spawn_timers.get_mut(entity) { + spawn_timer.timer -= context.delta; + if spawn_timer.timer <= 0.0 { + spawn_timer.reset_timer(); + let slime_count = context.entities.components::().len(); + if slime_count < spawn_timer.max_allowed { + context.event_publisher.queue(Event::SpawnSlimeRandomly); + } + } + } + } } fn update_system_camera_follows_player(context: &mut Core) { - if let Some((player_entity, _)) = context.entities.components::().single() { - if let Some((_, mut camera)) = context.entities.components_mut::().single_mut() { - let positions = context.entities.components::().unwrap(); - let position = positions.get(player_entity).unwrap(); + if let Some((player_entity, _)) = context.entities.components::().single() { + if let Some((_, mut camera)) = context.entities.components_mut::().single_mut() { + let positions = context.entities.components::().unwrap(); + let position = positions.get(player_entity).unwrap(); - let camera_x = position.0.x as i32 - (SCREEN_WIDTH as i32 / 2) + 8; - let camera_y = position.0.y as i32 - (SCREEN_HEIGHT as i32 / 2) + 8; + let camera_x = position.0.x as i32 - (SCREEN_WIDTH as i32 / 2) + 8; + let camera_y = position.0.y as i32 - (SCREEN_HEIGHT as i32 / 2) + 8; - // clamp camera position to the map boundaries - let map_pixel_width = context.tilemap.width() * TILE_WIDTH; - let map_pixel_height = context.tilemap.height() * TILE_HEIGHT; - let max_camera_x = map_pixel_width - SCREEN_WIDTH; - let max_camera_y = map_pixel_height - SCREEN_HEIGHT; + // clamp camera position to the map boundaries + let map_pixel_width = context.tilemap.width() * TILE_WIDTH; + let map_pixel_height = context.tilemap.height() * TILE_HEIGHT; + let max_camera_x = map_pixel_width - SCREEN_WIDTH; + let max_camera_y = map_pixel_height - SCREEN_HEIGHT; - camera.x = camera_x.clamp(0, max_camera_x as i32); - camera.y = camera_y.clamp(0, max_camera_y as i32); - } - } + camera.x = camera_x.clamp(0, max_camera_x as i32); + camera.y = camera_y.clamp(0, max_camera_y as i32); + } + } } fn update_system_turn_attached_entities(context: &mut Core) { - let attachments = context.entities.components::().unwrap(); - let mut facing_directions = context.entities.components_mut::(); + let attachments = context.entities.components::().unwrap(); + let mut facing_directions = context.entities.components_mut::(); - for (parent_entity, attachment) in attachments.iter() { - // the parent may not have a facing direction. and if so, we don't need to change the - // attachment (child) - let parent_facing_direction = if let Some(facing_direction) = facing_directions.get(&parent_entity) { - facing_direction.0 - } else { - continue; - }; + for (parent_entity, attachment) in attachments.iter() { + // the parent may not have a facing direction. and if so, we don't need to change the + // attachment (child) + let parent_facing_direction = if let Some(facing_direction) = facing_directions.get(&parent_entity) { + facing_direction.0 + } else { + continue; + }; - // change the direction of the attachment (child) to match the parent ... if the - // attachment even has a direction itself ... - if let Some(mut facing_direction) = facing_directions.get_mut(&attachment.0) { - facing_direction.0 = parent_facing_direction; - } - } + // change the direction of the attachment (child) to match the parent ... if the + // attachment even has a direction itself ... + if let Some(mut facing_direction) = facing_directions.get_mut(&attachment.0) { + facing_direction.0 = parent_facing_direction; + } + } } fn update_system_position_attached_entities(context: &mut Core) { - let attachments = context.entities.components::().unwrap(); - let mut positions = context.entities.components_mut::(); - let facing_directions = context.entities.components::(); - let offsets = context.entities.components::(); - let offset_by_directions = context.entities.components::(); + let attachments = context.entities.components::().unwrap(); + let mut positions = context.entities.components_mut::(); + let facing_directions = context.entities.components::(); + let offsets = context.entities.components::(); + let offset_by_directions = context.entities.components::(); - for (parent_entity, attachment) in attachments.iter() { - // get the parent position used as the base for the attached (child) entity. if the - // parent doesn't have one (probably it is dead?), then skip this attachment - let parent_position; - if let Some(position) = positions.get(&parent_entity) { - parent_position = position.0; - } else { - continue; - } + for (parent_entity, attachment) in attachments.iter() { + // get the parent position used as the base for the attached (child) entity. if the + // parent doesn't have one (probably it is dead?), then skip this attachment + let parent_position; + if let Some(position) = positions.get(&parent_entity) { + parent_position = position.0; + } else { + continue; + } - let attached_entity = attachment.0; - if let Some(mut attachment_position) = positions.get_mut(&attached_entity) { - // start off the attachment by placing it directly at the parent - attachment_position.0 = parent_position; + let attached_entity = attachment.0; + if let Some(mut attachment_position) = positions.get_mut(&attached_entity) { + // start off the attachment by placing it directly at the parent + attachment_position.0 = parent_position; - // then add whatever position offset it needs - if let Some(offset) = offsets.get(&attached_entity) { - attachment_position.0 += offset.0; - } else if let Some(offset_by_direction) = offset_by_directions.get(&attached_entity) { - if let Some(facing_direction) = facing_directions.get(&attached_entity) { - attachment_position.0 += offset_by_direction.offsets[facing_direction.0 as usize]; - } - } - } - } + // then add whatever position offset it needs + if let Some(offset) = offsets.get(&attached_entity) { + attachment_position.0 += offset.0; + } else if let Some(offset_by_direction) = offset_by_directions.get(&attached_entity) { + if let Some(facing_direction) = facing_directions.get(&attached_entity) { + attachment_position.0 += offset_by_direction.offsets[facing_direction.0 as usize]; + } + } + } + } } fn update_system_timed_flicker(context: &mut Core) { - let mut timed_flickers = context.entities.components_mut::().unwrap(); - for (_, flicker) in timed_flickers.iter_mut() { - flicker.update(context.delta); - } - timed_flickers.retain(|_, flicker| flicker.timer > 0.0); + let mut timed_flickers = context.entities.components_mut::().unwrap(); + for (_, flicker) in timed_flickers.iter_mut() { + flicker.update(context.delta); + } + timed_flickers.retain(|_, flicker| flicker.timer > 0.0); } fn update_system_pickups(context: &mut Core) { - let mut pickupables = context.entities.components_mut::().unwrap(); - let pickupers = context.entities.components::().unwrap(); - let positions = context.entities.components::(); - let bounds = context.entities.components::(); + let mut pickupables = context.entities.components_mut::().unwrap(); + let pickupers = context.entities.components::().unwrap(); + let positions = context.entities.components::(); + let bounds = context.entities.components::(); - // don't really think this pre_timer thing is necessary anymore ... ? - for (_, pickupable) in pickupables.iter_mut() { - if pickupable.pre_timer > 0.0 { - pickupable.pre_timer -= context.delta; - } - } + // don't really think this pre_timer thing is necessary anymore ... ? + for (_, pickupable) in pickupables.iter_mut() { + if pickupable.pre_timer > 0.0 { + pickupable.pre_timer -= context.delta; + } + } - // TODO: this is slow + // TODO: this is slow - for (pickuper_entity, _) in pickupers.iter() { - let pickuper_position = positions.get(pickuper_entity).unwrap(); - let pickuper_bounds = bounds.get(pickuper_entity).unwrap(); - let pickuper_circle = Circle::new( - pickuper_position.0.x as i32 + pickuper_bounds.width as i32 / 2, - pickuper_position.0.y as i32 + pickuper_bounds.height as i32 / 2, - pickuper_bounds.radius - ); + for (pickuper_entity, _) in pickupers.iter() { + let pickuper_position = positions.get(pickuper_entity).unwrap(); + let pickuper_bounds = bounds.get(pickuper_entity).unwrap(); + let pickuper_circle = Circle::new( + pickuper_position.0.x as i32 + pickuper_bounds.width as i32 / 2, + pickuper_position.0.y as i32 + pickuper_bounds.height as i32 / 2, + pickuper_bounds.radius, + ); - for (pickupable_entity, pickupable) in pickupables.iter() { - if pickupable.pre_timer <= 0.0 { - let pickupable_position = positions.get(pickupable_entity).unwrap(); - let pickupable_bounds = bounds.get(pickupable_entity).unwrap(); - let pickupable_circle = Circle::new( - pickupable_position.0.x as i32 + pickupable_bounds.width as i32 / 2, - pickupable_position.0.y as i32 + pickupable_bounds.height as i32 / 2, - pickupable_bounds.radius - ); + for (pickupable_entity, pickupable) in pickupables.iter() { + if pickupable.pre_timer <= 0.0 { + let pickupable_position = positions.get(pickupable_entity).unwrap(); + let pickupable_bounds = bounds.get(pickupable_entity).unwrap(); + let pickupable_circle = Circle::new( + pickupable_position.0.x as i32 + pickupable_bounds.width as i32 / 2, + pickupable_position.0.y as i32 + pickupable_bounds.height as i32 / 2, + pickupable_bounds.radius, + ); - if pickupable_circle.overlaps(&pickuper_circle) { - context.event_publisher.queue(Event::Pickup(*pickuper_entity, *pickupable_entity)); - } - } - } - } + if pickupable_circle.overlaps(&pickuper_circle) { + context.event_publisher.queue(Event::Pickup(*pickuper_entity, *pickupable_entity)); + } + } + } + } } /////////////////////////////////////////////////////////////////////////////////////////////////// fn render_system_sprites(context: &mut Core) { - context.sprite_render_list.clear(); + context.sprite_render_list.clear(); - let sprites = context.entities.components::().unwrap(); - let positions = context.entities.components::().unwrap(); - let timed_flickers = context.entities.components::().unwrap(); + let sprites = context.entities.components::().unwrap(); + let positions = context.entities.components::().unwrap(); + let timed_flickers = context.entities.components::().unwrap(); - if let Some((_, camera)) = context.entities.components::().single() { - // build up list of entities to be rendered with their positions so we can sort them - // and render these entities with a proper y-based sort order - for (entity, _) in sprites.iter() { - let mut blit_method = BlitMethod::Transparent(0); + if let Some((_, camera)) = context.entities.components::().single() { + // build up list of entities to be rendered with their positions so we can sort them + // and render these entities with a proper y-based sort order + for (entity, _) in sprites.iter() { + let mut blit_method = BlitMethod::Transparent(0); - // check for flicker effects - if let Some(flicker) = timed_flickers.get(entity) { - if !flicker.flick { - match flicker.method { - FlickerMethod::OnOff => { - // skip to the next entity, this one isn't visible - continue; - }, - FlickerMethod::Color(draw_color) => { - blit_method = BlitMethod::TransparentSingle { - transparent_color: 0, - draw_color - }; - } - } - } - } + // check for flicker effects + if let Some(flicker) = timed_flickers.get(entity) { + if !flicker.flick { + match flicker.method { + FlickerMethod::OnOff => { + // skip to the next entity, this one isn't visible + continue; + } + FlickerMethod::Color(draw_color) => { + blit_method = BlitMethod::TransparentSingle { + transparent_color: 0, + draw_color, + }; + } + } + } + } - let position = positions.get(entity).unwrap(); - context.sprite_render_list.push((*entity, position.0, blit_method)); - } - context.sprite_render_list.sort_unstable_by(|a, b| (a.1.y as i32).cmp(&(b.1.y as i32))); + let position = positions.get(entity).unwrap(); + context.sprite_render_list.push((*entity, position.0, blit_method)); + } + context.sprite_render_list.sort_unstable_by(|a, b| (a.1.y as i32).cmp(&(b.1.y as i32))); - // now render them in the correct order ... - for (entity, position, blit_method) in context.sprite_render_list.iter() { - let sprite = sprites.get(entity).unwrap(); - context.system.video.blit_atlas( - blit_method.clone(), - &sprite.atlas, - sprite.index, - position.x as i32 - camera.x, - position.y as i32 - camera.y, - ); - } - } + // now render them in the correct order ... + for (entity, position, blit_method) in context.sprite_render_list.iter() { + let sprite = sprites.get(entity).unwrap(); + context.system.video.blit_atlas( + blit_method.clone(), + &sprite.atlas, + sprite.index, + position.x as i32 - camera.x, + position.y as i32 - camera.y, + ); + } + } } fn render_system_pixels(context: &mut Core) { - let pixels = context.entities.components::().unwrap(); - let positions = context.entities.components::(); + let pixels = context.entities.components::().unwrap(); + let positions = context.entities.components::(); - if let Some((_, camera)) = context.entities.components::().single() { - for (entity, pixel) in pixels.iter() { - if let Some(position) = positions.get(entity) { - context.system.video.set_pixel( - position.0.x as i32 - camera.x, - position.0.y as i32 - camera.y, - pixel.0, - ); - } - } - } + if let Some((_, camera)) = context.entities.components::().single() { + for (entity, pixel) in pixels.iter() { + if let Some(position) = positions.get(entity) { + context.system.video.set_pixel( + position.0.x as i32 - camera.x, + position.0.y as i32 - camera.y, + pixel.0, + ); + } + } + } } pub fn init_component_system(cs: &mut ComponentSystems) { - cs.reset(); - cs.add_update_system(update_system_lifetime); - cs.add_update_system(update_system_current_entity_activity); - cs.add_update_system(update_system_walking_time); - cs.add_update_system(update_system_pushing); - cs.add_update_system(update_system_movement); - cs.add_update_system(update_system_turn_attached_entities); - cs.add_update_system(update_system_position_attached_entities); - cs.add_update_system(update_system_friction); - cs.add_update_system(update_system_force_decay); - cs.add_update_system(update_system_randomly_walk_around); - cs.add_update_system(update_system_animation); - cs.add_update_system(update_system_set_sprite_index_from_animation); - cs.add_update_system(update_system_set_sprite_index_by_direction); - cs.add_update_system(update_system_randomly_spawn_slimes); - cs.add_update_system(update_system_camera_follows_player); - cs.add_update_system(update_system_timed_flicker); - cs.add_update_system(update_system_pickups); - cs.add_render_system(render_system_sprites); - cs.add_render_system(render_system_pixels); + cs.reset(); + cs.add_update_system(update_system_lifetime); + cs.add_update_system(update_system_current_entity_activity); + cs.add_update_system(update_system_walking_time); + cs.add_update_system(update_system_pushing); + cs.add_update_system(update_system_movement); + cs.add_update_system(update_system_turn_attached_entities); + cs.add_update_system(update_system_position_attached_entities); + cs.add_update_system(update_system_friction); + cs.add_update_system(update_system_force_decay); + cs.add_update_system(update_system_randomly_walk_around); + cs.add_update_system(update_system_animation); + cs.add_update_system(update_system_set_sprite_index_from_animation); + cs.add_update_system(update_system_set_sprite_index_by_direction); + cs.add_update_system(update_system_randomly_spawn_slimes); + cs.add_update_system(update_system_camera_follows_player); + cs.add_update_system(update_system_timed_flicker); + cs.add_update_system(update_system_pickups); + cs.add_render_system(render_system_sprites); + cs.add_render_system(render_system_pixels); } diff --git a/examples/slimed/src/main.rs b/examples/slimed/src/main.rs index a833192..18bc49d 100644 --- a/examples/slimed/src/main.rs +++ b/examples/slimed/src/main.rs @@ -27,176 +27,176 @@ pub const TILE_WIDTH: u32 = 16; pub const TILE_HEIGHT: u32 = 16; pub struct Core { - pub delta: f32, - pub system: System, - pub font: BitmaskFont, - pub entities: Entities, - pub event_publisher: EventPublisher, - pub palette: Palette, - pub fade_out_palette: Palette, - pub tiles: Rc, - pub hero_male: Rc, - pub hero_female: Rc, - pub green_slime: Rc, - pub blue_slime: Rc, - pub orange_slime: Rc, - pub fist: Rc, - pub sword: Rc, - pub particles: Rc, - pub items: Rc, - pub ui: Rc, - pub tilemap: TileMap, - pub slime_activity_states: Rc>>, - pub hero_activity_states: Rc>>, - pub poof1_animation_def: Rc, - pub poof2_animation_def: Rc, - pub sparkles_animation_def: Rc, - pub sprite_render_list: Vec<(EntityId, Vector2, BlitMethod)>, + pub delta: f32, + pub system: System, + pub font: BitmaskFont, + pub entities: Entities, + pub event_publisher: EventPublisher, + pub palette: Palette, + pub fade_out_palette: Palette, + pub tiles: Rc, + pub hero_male: Rc, + pub hero_female: Rc, + pub green_slime: Rc, + pub blue_slime: Rc, + pub orange_slime: Rc, + pub fist: Rc, + pub sword: Rc, + pub particles: Rc, + pub items: Rc, + pub ui: Rc, + pub tilemap: TileMap, + pub slime_activity_states: Rc>>, + pub hero_activity_states: Rc>>, + pub poof1_animation_def: Rc, + pub poof2_animation_def: Rc, + pub sparkles_animation_def: Rc, + pub sprite_render_list: Vec<(EntityId, Vector2, BlitMethod)>, } impl CoreState for Core { - fn system(&self) -> &System { - &self.system - } + fn system(&self) -> &System { + &self.system + } - fn system_mut(&mut self) -> &mut System { - &mut self.system - } + fn system_mut(&mut self) -> &mut System { + &mut self.system + } - fn delta(&self) -> f32 { - self.delta - } + fn delta(&self) -> f32 { + self.delta + } - fn set_delta(&mut self, delta: f32) { - self.delta = delta; - } + fn set_delta(&mut self, delta: f32) { + self.delta = delta; + } } impl CoreStateWithEvents for Core { - fn event_publisher(&mut self) -> &mut EventPublisher { - &mut self.event_publisher - } + fn event_publisher(&mut self) -> &mut EventPublisher { + &mut self.event_publisher + } } pub struct Support { - pub component_systems: ComponentSystems, - pub event_listeners: EventListeners, + pub component_systems: ComponentSystems, + pub event_listeners: EventListeners, } impl SupportSystems for Support {} impl SupportSystemsWithEvents for Support { - type ContextType = Core; + type ContextType = Core; - fn event_listeners(&mut self) -> &mut EventListeners { - &mut self.event_listeners - } + fn event_listeners(&mut self) -> &mut EventListeners { + &mut self.event_listeners + } } pub struct Game { - pub core: Core, - pub support: Support, + pub core: Core, + pub support: Support, } impl AppContext for Game { - type CoreType = Core; - type SupportType = Support; + type CoreType = Core; + type SupportType = Support; - fn core(&mut self) -> &mut Self::CoreType { - &mut self.core - } + fn core(&mut self) -> &mut Self::CoreType { + &mut self.core + } - fn support(&mut self) -> &mut Self::SupportType { - &mut self.support - } + fn support(&mut self) -> &mut Self::SupportType { + &mut self.support + } } impl Game { - pub fn new(mut system: System) -> Result { - let palette = load_palette(Path::new("./assets/db16.pal"))?; - system.palette = palette.clone(); + pub fn new(mut system: System) -> Result { + let palette = load_palette(Path::new("./assets/db16.pal"))?; + system.palette = palette.clone(); - let font = load_font(Path::new("./assets/dp.fnt"))?; + let font = load_font(Path::new("./assets/dp.fnt"))?; - let tiles = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/tiles.pcx"))?); - let hero_male = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/hero_male.pcx"))?); - let hero_female = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/hero_female.pcx"))?); - let green_slime = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/green_slime.pcx"))?); - let blue_slime = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/blue_slime.pcx"))?); - let orange_slime = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/orange_slime.pcx"))?); - let fist = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/fist.pcx"))?); - let sword = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/sword.pcx"))?); - let particles = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/particles.pcx"))?); - let items = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/items.pcx"))?); + let tiles = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/tiles.pcx"))?); + let hero_male = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/hero_male.pcx"))?); + let hero_female = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/hero_female.pcx"))?); + let green_slime = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/green_slime.pcx"))?); + let blue_slime = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/blue_slime.pcx"))?); + let orange_slime = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/orange_slime.pcx"))?); + let fist = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/fist.pcx"))?); + let sword = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/sword.pcx"))?); + let particles = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/particles.pcx"))?); + let items = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/items.pcx"))?); - let mut ui = load_bitmap_atlas(Path::new("./assets/ui.pcx"))?; - ui.add(Rect::new(0, 0, 16, 16))?; - ui.add(Rect::new(16, 0, 16, 16))?; - for i in 0..8 { - ui.add(Rect::new(i * 8, 16, 8, 8))?; - } + let mut ui = load_bitmap_atlas(Path::new("./assets/ui.pcx"))?; + ui.add(Rect::new(0, 0, 16, 16))?; + ui.add(Rect::new(16, 0, 16, 16))?; + for i in 0..8 { + ui.add(Rect::new(i * 8, 16, 8, 8))?; + } - let tilemap = TileMap::load_from(Path::new("./assets/title_screen.map.json"))?; + let tilemap = TileMap::load_from(Path::new("./assets/title_screen.map.json"))?; - let entities = Entities::new(); - let component_systems = ComponentSystems::new(); - let event_publisher = EventPublisher::new(); - let event_listeners = EventListeners::new(); + let entities = Entities::new(); + let component_systems = ComponentSystems::new(); + let event_publisher = EventPublisher::new(); + let event_listeners = EventListeners::new(); - let slime_activity_states = HashMap::from([ - (EntityActivity::Idle, Rc::new(AnimationDef::new(&[1, 2], true, 1.0, Some(3)))), - (EntityActivity::Walking, Rc::new(AnimationDef::new(&[1, 0, 2, 0], true, 0.25, Some(3)))), - (EntityActivity::Attacking, Rc::new(AnimationDef::new(&[0], false, 0.3, Some(3)))), - (EntityActivity::Dead, Rc::new(AnimationDef::new(&[12], false, 1.0, None))), - ]); - let hero_activity_states = HashMap::from([ - (EntityActivity::Idle, Rc::new(AnimationDef::new(&[0], true, 0.5, Some(4)))), - (EntityActivity::Walking, Rc::new(AnimationDef::new(&[0, 1, 0, 2], true, 0.15, Some(4)))), - (EntityActivity::Attacking, Rc::new(AnimationDef::new(&[3], false, 0.3, Some(4)))), - (EntityActivity::Dead, Rc::new(AnimationDef::new(&[16], false, 1.0, None))), - ]); - let poof1_animation_def = Rc::new(AnimationDef::new(&[0, 1, 2], false, 0.15, None)); - let poof2_animation_def = Rc::new(AnimationDef::new(&[3, 4, 5], false, 0.15, None)); - let sparkles_animation_def = Rc::new(AnimationDef::new(&[6, 7, 8, 9], false, 0.1, None)); + let slime_activity_states = HashMap::from([ + (EntityActivity::Idle, Rc::new(AnimationDef::new(&[1, 2], true, 1.0, Some(3)))), + (EntityActivity::Walking, Rc::new(AnimationDef::new(&[1, 0, 2, 0], true, 0.25, Some(3)))), + (EntityActivity::Attacking, Rc::new(AnimationDef::new(&[0], false, 0.3, Some(3)))), + (EntityActivity::Dead, Rc::new(AnimationDef::new(&[12], false, 1.0, None))), + ]); + let hero_activity_states = HashMap::from([ + (EntityActivity::Idle, Rc::new(AnimationDef::new(&[0], true, 0.5, Some(4)))), + (EntityActivity::Walking, Rc::new(AnimationDef::new(&[0, 1, 0, 2], true, 0.15, Some(4)))), + (EntityActivity::Attacking, Rc::new(AnimationDef::new(&[3], false, 0.3, Some(4)))), + (EntityActivity::Dead, Rc::new(AnimationDef::new(&[16], false, 1.0, None))), + ]); + let poof1_animation_def = Rc::new(AnimationDef::new(&[0, 1, 2], false, 0.15, None)); + let poof2_animation_def = Rc::new(AnimationDef::new(&[3, 4, 5], false, 0.15, None)); + let sparkles_animation_def = Rc::new(AnimationDef::new(&[6, 7, 8, 9], false, 0.1, None)); - Ok(Game { - core: Core { - delta: 0.0, - system, - font, - entities, - event_publisher, - palette, - fade_out_palette: Palette::new_with_default(20, 12, 28), - tiles, - hero_male, - hero_female, - green_slime, - blue_slime, - orange_slime, - fist, - sword, - particles, - items, - ui: Rc::new(ui), - tilemap, - slime_activity_states: Rc::new(slime_activity_states), - hero_activity_states: Rc::new(hero_activity_states), - poof1_animation_def, - poof2_animation_def, - sparkles_animation_def, - sprite_render_list: Vec::with_capacity(1024), - }, - support: Support { - component_systems, - event_listeners, - }, - }) - } + Ok(Game { + core: Core { + delta: 0.0, + system, + font, + entities, + event_publisher, + palette, + fade_out_palette: Palette::new_with_default(20, 12, 28), + tiles, + hero_male, + hero_female, + green_slime, + blue_slime, + orange_slime, + fist, + sword, + particles, + items, + ui: Rc::new(ui), + tilemap, + slime_activity_states: Rc::new(slime_activity_states), + hero_activity_states: Rc::new(hero_activity_states), + poof1_animation_def, + poof2_animation_def, + sparkles_animation_def, + sprite_render_list: Vec::with_capacity(1024), + }, + support: Support { + component_systems, + event_listeners, + }, + }) + } } fn main() -> Result<()> { - let system = SystemBuilder::new().window_title("Slime Stabbing Simulator").vsync(true).build()?; - let game = Game::new(system)?; - main_loop(game, MainMenuState::new()).context("Main loop error") + let system = SystemBuilder::new().window_title("Slime Stabbing Simulator").vsync(true).build()?; + let game = Game::new(system)?; + main_loop(game, MainMenuState::new()).context("Main loop error") } diff --git a/examples/slimed/src/states.rs b/examples/slimed/src/states.rs index 831713f..7b077b6 100644 --- a/examples/slimed/src/states.rs +++ b/examples/slimed/src/states.rs @@ -11,195 +11,195 @@ use crate::Game; use crate::support::*; pub struct MainMenuState { - fade: f32, - selection: i32, + fade: f32, + selection: i32, } impl MainMenuState { - pub fn new() -> Self { - MainMenuState { - fade: 0.0, - selection: 0, - } - } + pub fn new() -> Self { + MainMenuState { + fade: 0.0, + selection: 0, + } + } } impl AppState for MainMenuState { - fn update(&mut self, state: State, context: &mut Game) -> Option> { - if state == State::Active { - if context.core.system.input_devices.keyboard.is_key_pressed(Scancode::Escape) { - return Some(StateChange::Pop(1)); - } - if context.core.system.input_devices.keyboard.is_key_pressed(Scancode::Up) { - self.selection = (self.selection - 1).clamp(0, 1); - } - if context.core.system.input_devices.keyboard.is_key_pressed(Scancode::Down) { - self.selection = (self.selection + 1).clamp(0, 1); - } + fn update(&mut self, state: State, context: &mut Game) -> Option> { + if state == State::Active { + if context.core.system.input_devices.keyboard.is_key_pressed(Scancode::Escape) { + return Some(StateChange::Pop(1)); + } + if context.core.system.input_devices.keyboard.is_key_pressed(Scancode::Up) { + self.selection = (self.selection - 1).clamp(0, 1); + } + if context.core.system.input_devices.keyboard.is_key_pressed(Scancode::Down) { + self.selection = (self.selection + 1).clamp(0, 1); + } - if context.core.system.input_devices.keyboard.is_key_pressed(Scancode::Return) { - match self.selection { - 0 => return Some(StateChange::Push(Box::new(GamePlayState::new()))), - 1 => return Some(StateChange::Pop(1)), - _ => {} - } - } - } + if context.core.system.input_devices.keyboard.is_key_pressed(Scancode::Return) { + match self.selection { + 0 => return Some(StateChange::Push(Box::new(GamePlayState::new()))), + 1 => return Some(StateChange::Pop(1)), + _ => {} + } + } + } - context.support.do_events(&mut context.core); - context.support.component_systems.update(&mut context.core); + context.support.do_events(&mut context.core); + context.support.component_systems.update(&mut context.core); - None - } + None + } - fn render(&mut self, state: State, context: &mut Game) { - context.core.tilemap.draw(&mut context.core.system.video, &context.core.tiles, 0, 0); - context.support.component_systems.render(&mut context.core); + fn render(&mut self, state: State, context: &mut Game) { + context.core.tilemap.draw(&mut context.core.system.video, &context.core.tiles, 0, 0); + context.support.component_systems.render(&mut context.core); - let x = 32; - let y = 160; - let width = 48; - let height = 40; - const SPACER: i32 = 8; - draw_window(&mut context.core.system.video, &context.core.ui, x, y, x + width, y + height); + let x = 32; + let y = 160; + let width = 48; + let height = 40; + const SPACER: i32 = 8; + draw_window(&mut context.core.system.video, &context.core.ui, x, y, x + width, y + height); - let selection_y = y + SPACER + (self.selection as i32 * 16); - context.core.system.video.print_string(">", x + SPACER, selection_y, FontRenderOpts::Color(15), &context.core.font); + let selection_y = y + SPACER + (self.selection as i32 * 16); + context.core.system.video.print_string(">", x + SPACER, selection_y, FontRenderOpts::Color(15), &context.core.font); - context.core.system.video.print_string("Play", x + SPACER + SPACER, y + SPACER, FontRenderOpts::Color(15), &context.core.font); - context.core.system.video.print_string("Quit", x + SPACER + SPACER, y + SPACER + 16, FontRenderOpts::Color(15), &context.core.font); - } + context.core.system.video.print_string("Play", x + SPACER + SPACER, y + SPACER, FontRenderOpts::Color(15), &context.core.font); + context.core.system.video.print_string("Quit", x + SPACER + SPACER, y + SPACER + 16, FontRenderOpts::Color(15), &context.core.font); + } - fn transition(&mut self, state: State, context: &mut Game) -> bool { - update_fade_transition(state, &mut self.fade, context.core.delta * 3.0, context) - } + fn transition(&mut self, state: State, context: &mut Game) -> bool { + update_fade_transition(state, &mut self.fade, context.core.delta * 3.0, context) + } - fn state_change(&mut self, new_state: State, old_state: State, context: &mut Game) { - match new_state { - State::Pending | State::Resume => { - init_everything(context, Path::new("./assets/title_screen.map.json"), 0.2, 1.0, 32); - } - State::TransitionIn => { - self.fade = 0.0; - } - State::TransitionOut(_) => { - self.fade = 1.0; - } - State::Paused => { - context.core.system.palette = context.core.palette.clone(); - } - _ => {} - } - } + fn state_change(&mut self, new_state: State, old_state: State, context: &mut Game) { + match new_state { + State::Pending | State::Resume => { + init_everything(context, Path::new("./assets/title_screen.map.json"), 0.2, 1.0, 32); + } + State::TransitionIn => { + self.fade = 0.0; + } + State::TransitionOut(_) => { + self.fade = 1.0; + } + State::Paused => { + context.core.system.palette = context.core.palette.clone(); + } + _ => {} + } + } } pub struct GamePlayState { - fade: f32, - in_menu: bool, - selection: i32, + fade: f32, + in_menu: bool, + selection: i32, } impl GamePlayState { - pub fn new() -> Self { - GamePlayState { - fade: 0.0, - in_menu: false, - selection: 0, - } - } + pub fn new() -> Self { + GamePlayState { + fade: 0.0, + in_menu: false, + selection: 0, + } + } } impl AppState for GamePlayState { - fn update(&mut self, state: State, context: &mut Game) -> Option> { - if state == State::Active { - if self.in_menu { - if context.core.system.input_devices.keyboard.is_key_pressed(Scancode::Escape) { - self.in_menu = false; - } - if context.core.system.input_devices.keyboard.is_key_pressed(Scancode::Up) { - self.selection = (self.selection - 1).clamp(0, 1); - } - if context.core.system.input_devices.keyboard.is_key_pressed(Scancode::Down) { - self.selection = (self.selection + 1).clamp(0, 1); - } + fn update(&mut self, state: State, context: &mut Game) -> Option> { + if state == State::Active { + if self.in_menu { + if context.core.system.input_devices.keyboard.is_key_pressed(Scancode::Escape) { + self.in_menu = false; + } + if context.core.system.input_devices.keyboard.is_key_pressed(Scancode::Up) { + self.selection = (self.selection - 1).clamp(0, 1); + } + if context.core.system.input_devices.keyboard.is_key_pressed(Scancode::Down) { + self.selection = (self.selection + 1).clamp(0, 1); + } - if context.core.system.input_devices.keyboard.is_key_pressed(Scancode::Return) { - match self.selection { - 0 => self.in_menu = false, - 1 => return Some(StateChange::Pop(1)), - _ => {} - } - } - } else { - if context.core.system.input_devices.keyboard.is_key_pressed(Scancode::Escape) { - self.in_menu = true; - } + if context.core.system.input_devices.keyboard.is_key_pressed(Scancode::Return) { + match self.selection { + 0 => self.in_menu = false, + 1 => return Some(StateChange::Pop(1)), + _ => {} + } + } + } else { + if context.core.system.input_devices.keyboard.is_key_pressed(Scancode::Escape) { + self.in_menu = true; + } - if let Some((player_entity, _)) = context.core.entities.components::().single() { - if context.core.system.input_devices.keyboard.is_key_down(Scancode::Up) { - context.core.event_publisher.queue(Event::TurnAndMove(*player_entity, Direction::North)); - } - if context.core.system.input_devices.keyboard.is_key_down(Scancode::Down) { - context.core.event_publisher.queue(Event::TurnAndMove(*player_entity, Direction::South)); - } - if context.core.system.input_devices.keyboard.is_key_down(Scancode::Left) { - context.core.event_publisher.queue(Event::TurnAndMove(*player_entity, Direction::West)); - } - if context.core.system.input_devices.keyboard.is_key_down(Scancode::Right) { - context.core.event_publisher.queue(Event::TurnAndMove(*player_entity, Direction::East)); - } - if context.core.system.input_devices.keyboard.is_key_pressed(Scancode::Space) { - context.core.event_publisher.queue(Event::Attack(*player_entity)); - } - } - } - } + if let Some((player_entity, _)) = context.core.entities.components::().single() { + if context.core.system.input_devices.keyboard.is_key_down(Scancode::Up) { + context.core.event_publisher.queue(Event::TurnAndMove(*player_entity, Direction::North)); + } + if context.core.system.input_devices.keyboard.is_key_down(Scancode::Down) { + context.core.event_publisher.queue(Event::TurnAndMove(*player_entity, Direction::South)); + } + if context.core.system.input_devices.keyboard.is_key_down(Scancode::Left) { + context.core.event_publisher.queue(Event::TurnAndMove(*player_entity, Direction::West)); + } + if context.core.system.input_devices.keyboard.is_key_down(Scancode::Right) { + context.core.event_publisher.queue(Event::TurnAndMove(*player_entity, Direction::East)); + } + if context.core.system.input_devices.keyboard.is_key_pressed(Scancode::Space) { + context.core.event_publisher.queue(Event::Attack(*player_entity)); + } + } + } + } - context.support.do_events(&mut context.core); - context.support.component_systems.update(&mut context.core); + context.support.do_events(&mut context.core); + context.support.component_systems.update(&mut context.core); - None - } + None + } - fn render(&mut self, state: State, context: &mut Game) { - if let Some((_, camera)) = context.core.entities.components::().single() { - context.core.tilemap.draw(&mut context.core.system.video, &context.core.tiles, camera.x, camera.y); - } - context.support.component_systems.render(&mut context.core); + fn render(&mut self, state: State, context: &mut Game) { + if let Some((_, camera)) = context.core.entities.components::().single() { + context.core.tilemap.draw(&mut context.core.system.video, &context.core.tiles, camera.x, camera.y); + } + context.support.component_systems.render(&mut context.core); - if self.in_menu { - let x = 32; - let y = 160; - let width = 80; - let height = 40; - const SPACER: i32 = 8; - draw_window(&mut context.core.system.video, &context.core.ui, x, y, x + width, y + height); + if self.in_menu { + let x = 32; + let y = 160; + let width = 80; + let height = 40; + const SPACER: i32 = 8; + draw_window(&mut context.core.system.video, &context.core.ui, x, y, x + width, y + height); - let selection_y = y + SPACER + (self.selection as i32 * 16); - context.core.system.video.print_string(">", x + SPACER, selection_y, FontRenderOpts::Color(15), &context.core.font); + let selection_y = y + SPACER + (self.selection as i32 * 16); + context.core.system.video.print_string(">", x + SPACER, selection_y, FontRenderOpts::Color(15), &context.core.font); - context.core.system.video.print_string("Continue", x + SPACER + SPACER, y + SPACER, FontRenderOpts::Color(15), &context.core.font); - context.core.system.video.print_string("Quit", x + SPACER + SPACER, y + SPACER + 16, FontRenderOpts::Color(15), &context.core.font); - } - } + context.core.system.video.print_string("Continue", x + SPACER + SPACER, y + SPACER, FontRenderOpts::Color(15), &context.core.font); + context.core.system.video.print_string("Quit", x + SPACER + SPACER, y + SPACER + 16, FontRenderOpts::Color(15), &context.core.font); + } + } - fn transition(&mut self, state: State, context: &mut Game) -> bool { - update_fade_transition(state, &mut self.fade, context.core.delta * 3.0, context) - } + fn transition(&mut self, state: State, context: &mut Game) -> bool { + update_fade_transition(state, &mut self.fade, context.core.delta * 3.0, context) + } - fn state_change(&mut self, new_state: State, old_state: State, context: &mut Game) { - match new_state { - State::Pending => { - init_everything(context, Path::new("./assets/arena.map.json"), 0.5, 2.0, 100); - spawn_player_randomly(&mut context.core); - }, - State::TransitionIn => { - self.fade = 0.0; - } - State::TransitionOut(_) => { - self.fade = 1.0; - } - _ => {} - } - } + fn state_change(&mut self, new_state: State, old_state: State, context: &mut Game) { + match new_state { + State::Pending => { + init_everything(context, Path::new("./assets/arena.map.json"), 0.5, 2.0, 100); + spawn_player_randomly(&mut context.core); + } + State::TransitionIn => { + self.fade = 0.0; + } + State::TransitionOut(_) => { + self.fade = 1.0; + } + _ => {} + } + } } diff --git a/examples/slimed/src/support.rs b/examples/slimed/src/support.rs index 0c657e8..55bb6b2 100644 --- a/examples/slimed/src/support.rs +++ b/examples/slimed/src/support.rs @@ -8,76 +8,76 @@ use libretrogd::states::*; use crate::{Game, TILE_HEIGHT, TILE_WIDTH}; pub fn load_palette(path: &Path) -> Result { - Palette::load_from_file(path, PaletteFormat::Vga).context(format!("Loading palette: {:?}", path)) + Palette::load_from_file(path, PaletteFormat::Vga).context(format!("Loading palette: {:?}", path)) } pub fn load_font(path: &Path) -> Result { - BitmaskFont::load_from_file(path).context(format!("Loading font: {:?}", path)) + BitmaskFont::load_from_file(path).context(format!("Loading font: {:?}", path)) } pub fn load_bitmap_atlas_autogrid(path: &Path) -> Result { - let (bmp, _) = Bitmap::load_file(path).context(format!("Loading bitmap atlas: {:?}", path))?; - let mut atlas = BitmapAtlas::new(bmp); - atlas.add_grid(TILE_WIDTH, TILE_HEIGHT)?; - Ok(atlas) + let (bmp, _) = Bitmap::load_file(path).context(format!("Loading bitmap atlas: {:?}", path))?; + let mut atlas = BitmapAtlas::new(bmp); + atlas.add_grid(TILE_WIDTH, TILE_HEIGHT)?; + Ok(atlas) } pub fn load_bitmap_atlas(path: &Path) -> Result { - let (bmp, _) = Bitmap::load_file(path).context(format!("Loading bitmap atlas: {:?}", path))?; - let atlas = BitmapAtlas::new(bmp); - Ok(atlas) + let (bmp, _) = Bitmap::load_file(path).context(format!("Loading bitmap atlas: {:?}", path))?; + let atlas = BitmapAtlas::new(bmp); + Ok(atlas) } pub fn draw_window(dest: &mut Bitmap, ui: &BitmapAtlas, left: i32, top: i32, right: i32, bottom: i32) { - dest.filled_rect(left + 8, top + 8, right - 8, bottom - 8, 1); + dest.filled_rect(left + 8, top + 8, right - 8, bottom - 8, 1); - // corners - dest.blit_region(BlitMethod::Transparent(0), &ui.bitmap(), &ui[2], left, top); - dest.blit_region(BlitMethod::Transparent(0), &ui.bitmap(), &ui[3], right - 8, top); - dest.blit_region(BlitMethod::Transparent(0), &ui.bitmap(), &ui[4], left, bottom - 8); - dest.blit_region(BlitMethod::Transparent(0), &ui.bitmap(), &ui[5], right - 8, bottom - 8); + // corners + dest.blit_region(BlitMethod::Transparent(0), &ui.bitmap(), &ui[2], left, top); + dest.blit_region(BlitMethod::Transparent(0), &ui.bitmap(), &ui[3], right - 8, top); + dest.blit_region(BlitMethod::Transparent(0), &ui.bitmap(), &ui[4], left, bottom - 8); + dest.blit_region(BlitMethod::Transparent(0), &ui.bitmap(), &ui[5], right - 8, bottom - 8); - // top and bottom edges - for i in 0..((right - left) / 8) - 2 { - let x = left + 8 + (i * 8); - dest.blit_region(BlitMethod::Transparent(0), &ui.bitmap(), &ui[9], x, top); - dest.blit_region(BlitMethod::Transparent(0), &ui.bitmap(), &ui[8], x, bottom - 8); - } + // top and bottom edges + for i in 0..((right - left) / 8) - 2 { + let x = left + 8 + (i * 8); + dest.blit_region(BlitMethod::Transparent(0), &ui.bitmap(), &ui[9], x, top); + dest.blit_region(BlitMethod::Transparent(0), &ui.bitmap(), &ui[8], x, bottom - 8); + } - // left and right edges - for i in 0..((bottom - top) / 8) - 2 { - let y = top + 8 + (i * 8); - dest.blit_region(BlitMethod::Transparent(0), &ui.bitmap(), &ui[6], left, y); - dest.blit_region(BlitMethod::Transparent(0), &ui.bitmap(), &ui[7], right - 8, y); - } + // left and right edges + for i in 0..((bottom - top) / 8) - 2 { + let y = top + 8 + (i * 8); + dest.blit_region(BlitMethod::Transparent(0), &ui.bitmap(), &ui[6], left, y); + dest.blit_region(BlitMethod::Transparent(0), &ui.bitmap(), &ui[7], right - 8, y); + } } pub fn update_fade_transition(state: State, fade: &mut f32, delta: f32, context: &mut Game) -> bool { - match state { - State::TransitionIn => { - *fade += delta; - if *fade >= 1.0 { - *fade = 1.0; - context.core.system.palette = context.core.palette.clone(); - true - } else { - context.core.system.palette.lerp(0..=255, &context.core.fade_out_palette, &context.core.palette, *fade); - false - } - }, - State::TransitionOut(_) => { - *fade -= delta; - if *fade <= 0.0 { - *fade = 0.0; - context.core.system.palette = context.core.fade_out_palette.clone(); - true - } else { - context.core.system.palette.lerp(0..=255, &context.core.fade_out_palette, &context.core.palette, *fade); - false - } - }, - _ => { - true - } - } + match state { + State::TransitionIn => { + *fade += delta; + if *fade >= 1.0 { + *fade = 1.0; + context.core.system.palette = context.core.palette.clone(); + true + } else { + context.core.system.palette.lerp(0..=255, &context.core.fade_out_palette, &context.core.palette, *fade); + false + } + } + State::TransitionOut(_) => { + *fade -= delta; + if *fade <= 0.0 { + *fade = 0.0; + context.core.system.palette = context.core.fade_out_palette.clone(); + true + } else { + context.core.system.palette.lerp(0..=255, &context.core.fade_out_palette, &context.core.palette, *fade); + false + } + } + _ => { + true + } + } } diff --git a/examples/slimed/src/tilemap.rs b/examples/slimed/src/tilemap.rs index fee8b9f..86a4803 100644 --- a/examples/slimed/src/tilemap.rs +++ b/examples/slimed/src/tilemap.rs @@ -17,111 +17,111 @@ pub const TILE_FLAG_SPAWNABLE: i32 = 1; #[derive(Debug, Deserialize)] pub struct TileMap { - width: u32, - height: u32, - layers: Vec>, + width: u32, + height: u32, + layers: Vec>, } impl TileMap { - pub fn load_from(path: &Path) -> Result { - let f = File::open(path)?; - let reader = BufReader::new(f); - serde_json::from_reader(reader).context(format!("Loading json tilemap: {:?}", path)) - } + pub fn load_from(path: &Path) -> Result { + let f = File::open(path)?; + let reader = BufReader::new(f); + serde_json::from_reader(reader).context(format!("Loading json tilemap: {:?}", path)) + } - #[inline] - pub fn width(&self) -> u32 { - self.width - } + #[inline] + pub fn width(&self) -> u32 { + self.width + } - #[inline] - pub fn height(&self) -> u32 { - self.height - } + #[inline] + pub fn height(&self) -> u32 { + self.height + } - #[inline] - pub fn index_to(&self, x: i32, y: i32) -> Option { - if x >= 0 && y >= 0 && x < self.width as i32 && y < self.height as i32 { - Some(((y * self.width as i32) + x) as usize) - } else { - None - } - } + #[inline] + pub fn index_to(&self, x: i32, y: i32) -> Option { + if x >= 0 && y >= 0 && x < self.width as i32 && y < self.height as i32 { + Some(((y * self.width as i32) + x) as usize) + } else { + None + } + } - #[inline] - pub fn lower(&self) -> &Box<[i32]> { - &self.layers[0] - } + #[inline] + pub fn lower(&self) -> &Box<[i32]> { + &self.layers[0] + } - #[inline] - pub fn upper(&self) -> &Box<[i32]> { - &self.layers[1] - } + #[inline] + pub fn upper(&self) -> &Box<[i32]> { + &self.layers[1] + } - #[inline] - pub fn collision(&self) -> &Box<[i32]> { - &self.layers[2] - } + #[inline] + pub fn collision(&self) -> &Box<[i32]> { + &self.layers[2] + } - pub fn draw(&self, dest: &mut Bitmap, tiles: &BitmapAtlas, camera_x: i32, camera_y: i32) { - let xt = camera_x / TILE_WIDTH as i32; - let yt = camera_y / TILE_HEIGHT as i32; - let xp = camera_x % TILE_WIDTH as i32; - let yp = camera_y % TILE_HEIGHT as i32; + pub fn draw(&self, dest: &mut Bitmap, tiles: &BitmapAtlas, camera_x: i32, camera_y: i32) { + let xt = camera_x / TILE_WIDTH as i32; + let yt = camera_y / TILE_HEIGHT as i32; + let xp = camera_x % TILE_WIDTH as i32; + let yp = camera_y % TILE_HEIGHT as i32; - for y in 0..=15 { - for x in 0..=20 { - if let Some(index) = self.index_to(x + xt, y + yt) { - let xd = (x * TILE_WIDTH as i32) - xp; - let yd = (y * TILE_HEIGHT as i32) - yp; + for y in 0..=15 { + for x in 0..=20 { + if let Some(index) = self.index_to(x + xt, y + yt) { + let xd = (x * TILE_WIDTH as i32) - xp; + let yd = (y * TILE_HEIGHT as i32) - yp; - let lower = self.layers[0][index]; - if lower >= 0 { - dest.blit_region(BlitMethod::Solid, tiles.bitmap(), &tiles[lower as usize], xd, yd); - } - let upper = self.layers[1][index]; - if upper >= 0 { - dest.blit_region(BlitMethod::Transparent(0), tiles.bitmap(), &tiles[upper as usize], xd, yd); - } - } - } - } - } + let lower = self.layers[0][index]; + if lower >= 0 { + dest.blit_region(BlitMethod::Solid, tiles.bitmap(), &tiles[lower as usize], xd, yd); + } + let upper = self.layers[1][index]; + if upper >= 0 { + dest.blit_region(BlitMethod::Transparent(0), tiles.bitmap(), &tiles[upper as usize], xd, yd); + } + } + } + } + } - pub fn is_colliding(&self, rect: &Rect) -> bool { - let x1 = rect.x / TILE_WIDTH as i32; - let y1 = rect.y / TILE_HEIGHT as i32; - let x2 = rect.right() / TILE_WIDTH as i32; - let y2 = rect.bottom() / TILE_HEIGHT as i32; + pub fn is_colliding(&self, rect: &Rect) -> bool { + let x1 = rect.x / TILE_WIDTH as i32; + let y1 = rect.y / TILE_HEIGHT as i32; + let x2 = rect.right() / TILE_WIDTH as i32; + let y2 = rect.bottom() / TILE_HEIGHT as i32; - for y in y1..=y2 { - for x in x1..=x2 { - match self.index_to(x, y) { - Some(index) => { - if self.collision()[index] == TILE_FLAG_COLLISION { - return true; - } - }, - None => return true - } - } - } - false - } + for y in y1..=y2 { + for x in x1..=x2 { + match self.index_to(x, y) { + Some(index) => { + if self.collision()[index] == TILE_FLAG_COLLISION { + return true; + } + } + None => return true + } + } + } + false + } - pub fn get_random_spawnable_coordinates(&self) -> (i32, i32) { - // TODO: do this better - let mut x; - let mut y; + pub fn get_random_spawnable_coordinates(&self) -> (i32, i32) { + // TODO: do this better + let mut x; + let mut y; - loop { - x = rnd_value(0, self.width as i32 - 1); - y = rnd_value(0, self.height as i32 - 1); - if self.collision()[self.index_to(x, y).unwrap()] == TILE_FLAG_SPAWNABLE { - break; - } - } + loop { + x = rnd_value(0, self.width as i32 - 1); + y = rnd_value(0, self.height as i32 - 1); + if self.collision()[self.index_to(x, y).unwrap()] == TILE_FLAG_SPAWNABLE { + break; + } + } - (x, y) - } + (x, y) + } } diff --git a/examples/template_complicated/src/main.rs b/examples/template_complicated/src/main.rs index 7150022..2641ac4 100644 --- a/examples/template_complicated/src/main.rs +++ b/examples/template_complicated/src/main.rs @@ -12,30 +12,30 @@ use libretrogd::utils::rnd_value; ////////////////////////////////////////////////////////////////////////////////////////////////// pub enum Event { - Remove(EntityId), - SpawnPixel, + Remove(EntityId), + SpawnPixel, } pub fn event_listener(event: &Event, context: &mut Core) -> bool { - match event { - Event::Remove(entity) => { - context.entities.remove_entity(*entity); - true - }, - Event::SpawnPixel => { - let speed = rnd_value(1, 10) as f32 * 10.0; - let angle = (rnd_value(0, 359) as f32).to_radians(); - let x = (SCREEN_WIDTH / 2) as f32; - let y = (SCREEN_HEIGHT / 2) as f32; - let color = rnd_value(0, 255); - let id = context.entities.new_entity(); - context.entities.add_component(id, Position(Vector2::new(x, y))); - context.entities.add_component(id, Velocity(Vector2::from_angle(angle) * speed)); - context.entities.add_component(id, Color(color)); - true - }, - _ => false - } + match event { + Event::Remove(entity) => { + context.entities.remove_entity(*entity); + true + } + Event::SpawnPixel => { + let speed = rnd_value(1, 10) as f32 * 10.0; + let angle = (rnd_value(0, 359) as f32).to_radians(); + let x = (SCREEN_WIDTH / 2) as f32; + let y = (SCREEN_HEIGHT / 2) as f32; + let color = rnd_value(0, 255); + let id = context.entities.new_entity(); + context.entities.add_component(id, Position(Vector2::new(x, y))); + context.entities.add_component(id, Velocity(Vector2::from_angle(angle) * speed)); + context.entities.add_component(id, Color(color)); + true + } + _ => false + } } ////////////////////////////////////////////////////////////////////////////////////////////////// @@ -49,32 +49,32 @@ pub struct Color(u8); ////////////////////////////////////////////////////////////////////////////////////////////////// pub fn update_system_movement(context: &mut Core) { - let mut positions = context.entities.components_mut::().unwrap(); - let velocities = context.entities.components::().unwrap(); + let mut positions = context.entities.components_mut::().unwrap(); + let velocities = context.entities.components::().unwrap(); - for (entity, position) in positions.iter_mut() { - let velocity = velocities.get(entity).unwrap(); - position.0 += velocity.0 * context.delta; - } + for (entity, position) in positions.iter_mut() { + let velocity = velocities.get(entity).unwrap(); + position.0 += velocity.0 * context.delta; + } } pub fn update_system_remove_offscreen(context: &mut Core) { - let positions = context.entities.components::().unwrap(); - for (entity, position) in positions.iter() { - if !context.system.video.is_xy_visible(position.0.x as i32, position.0.y as i32) { - context.event_publisher.queue(Event::Remove(*entity)); - } - } + let positions = context.entities.components::().unwrap(); + for (entity, position) in positions.iter() { + if !context.system.video.is_xy_visible(position.0.x as i32, position.0.y as i32) { + context.event_publisher.queue(Event::Remove(*entity)); + } + } } pub fn render_system_pixels(context: &mut Core) { - let positions = context.entities.components::().unwrap(); - let colors = context.entities.components::().unwrap(); + let positions = context.entities.components::().unwrap(); + let colors = context.entities.components::().unwrap(); - for (entity, position) in positions.iter() { - let color = colors.get(entity).unwrap(); - context.system.video.set_pixel(position.0.x as i32, position.0.y as i32, color.0); - } + for (entity, position) in positions.iter() { + let color = colors.get(entity).unwrap(); + context.system.video.set_pixel(position.0.x as i32, position.0.y as i32, color.0); + } } ////////////////////////////////////////////////////////////////////////////////////////////////// @@ -82,150 +82,150 @@ pub fn render_system_pixels(context: &mut Core) { pub struct DemoState; impl DemoState { - fn init(&mut self, context: &mut App) { - context.core.entities.init_components::(); - context.core.entities.init_components::(); - context.core.entities.init_components::(); + fn init(&mut self, context: &mut App) { + context.core.entities.init_components::(); + context.core.entities.init_components::(); + context.core.entities.init_components::(); - context.support.component_systems.reset(); - context.support.component_systems.add_update_system(update_system_movement); - context.support.component_systems.add_update_system(update_system_remove_offscreen); - context.support.component_systems.add_render_system(render_system_pixels); + context.support.component_systems.reset(); + context.support.component_systems.add_update_system(update_system_movement); + context.support.component_systems.add_update_system(update_system_remove_offscreen); + context.support.component_systems.add_render_system(render_system_pixels); - context.support.event_listeners.clear(); - context.support.event_listeners.add(event_listener); - } + context.support.event_listeners.clear(); + context.support.event_listeners.add(event_listener); + } } impl AppState for DemoState { - fn update(&mut self, state: State, context: &mut App) -> Option> { - if state == State::Active { - if context.core.system.input_devices.keyboard.is_key_pressed(Scancode::Escape) { - return Some(StateChange::Pop(1)) - } - } + fn update(&mut self, state: State, context: &mut App) -> Option> { + if state == State::Active { + if context.core.system.input_devices.keyboard.is_key_pressed(Scancode::Escape) { + return Some(StateChange::Pop(1)); + } + } - if rnd_value(0, 100) < 80 { - context.core.event_publisher.queue(Event::SpawnPixel); - } + if rnd_value(0, 100) < 80 { + context.core.event_publisher.queue(Event::SpawnPixel); + } - context.support.do_events(&mut context.core); - context.support.component_systems.update(&mut context.core); + context.support.do_events(&mut context.core); + context.support.component_systems.update(&mut context.core); - None - } + None + } - fn render(&mut self, state: State, context: &mut App) { - context.core.system.video.clear(0); - context.support.component_systems.render(&mut context.core); - } + fn render(&mut self, state: State, context: &mut App) { + context.core.system.video.clear(0); + context.support.component_systems.render(&mut context.core); + } - fn transition(&mut self, state: State, context: &mut App) -> bool { - true - } + fn transition(&mut self, state: State, context: &mut App) -> bool { + true + } - fn state_change(&mut self, new_state: State, old_state: State, context: &mut App) { - match new_state { - State::Pending => { - self.init(context); - }, - _ => {} - } - } + fn state_change(&mut self, new_state: State, old_state: State, context: &mut App) { + match new_state { + State::Pending => { + self.init(context); + } + _ => {} + } + } } ////////////////////////////////////////////////////////////////////////////////////////////////// pub struct Core { - pub delta: f32, - pub system: System, - pub entities: Entities, - pub event_publisher: EventPublisher, + pub delta: f32, + pub system: System, + pub entities: Entities, + pub event_publisher: EventPublisher, } impl CoreState for Core { - fn system(&self) -> &System { - &self.system - } + fn system(&self) -> &System { + &self.system + } - fn system_mut(&mut self) -> &mut System { - &mut self.system - } + fn system_mut(&mut self) -> &mut System { + &mut self.system + } - fn delta(&self) -> f32 { - self.delta - } + fn delta(&self) -> f32 { + self.delta + } - fn set_delta(&mut self, delta: f32) { - self.delta = delta; - } + fn set_delta(&mut self, delta: f32) { + self.delta = delta; + } } impl CoreStateWithEvents for Core { - fn event_publisher(&mut self) -> &mut EventPublisher { - &mut self.event_publisher - } + fn event_publisher(&mut self) -> &mut EventPublisher { + &mut self.event_publisher + } } pub struct Support { - pub component_systems: ComponentSystems, - pub event_listeners: EventListeners + pub component_systems: ComponentSystems, + pub event_listeners: EventListeners, } impl SupportSystems for Support {} impl SupportSystemsWithEvents for Support { - type ContextType = Core; + type ContextType = Core; - fn event_listeners(&mut self) -> &mut EventListeners { - &mut self.event_listeners - } + fn event_listeners(&mut self) -> &mut EventListeners { + &mut self.event_listeners + } } pub struct App { - pub core: Core, - pub support: Support, + pub core: Core, + pub support: Support, } impl AppContext for App { - type CoreType = Core; - type SupportType = Support; + type CoreType = Core; + type SupportType = Support; - fn core(&mut self) -> &mut Core { - &mut self.core - } + fn core(&mut self) -> &mut Core { + &mut self.core + } - fn support(&mut self) -> &mut Support { - &mut self.support - } + fn support(&mut self) -> &mut Support { + &mut self.support + } } impl App { - pub fn new(system: System) -> Result { - let entities = Entities::new(); - let component_systems = ComponentSystems::new(); - let event_publisher = EventPublisher::new(); - let event_listeners = EventListeners::new(); + pub fn new(system: System) -> Result { + let entities = Entities::new(); + let component_systems = ComponentSystems::new(); + let event_publisher = EventPublisher::new(); + let event_listeners = EventListeners::new(); - Ok(App { - core: Core { - delta: 0.0, - system, - entities, - event_publisher, - }, - support: Support { - component_systems, - event_listeners, - } - }) - } + Ok(App { + core: Core { + delta: 0.0, + system, + entities, + event_publisher, + }, + support: Support { + component_systems, + event_listeners, + }, + }) + } } ////////////////////////////////////////////////////////////////////////////////////////////////// fn main() -> Result<()> { - let system = SystemBuilder::new().window_title("Complicated Template").vsync(true).build()?; - let app = App::new(system)?; - main_loop(app, DemoState).context("Main loop error") + let system = SystemBuilder::new().window_title("Complicated Template").vsync(true).build()?; + let app = App::new(system)?; + main_loop(app, DemoState).context("Main loop error") } diff --git a/examples/template_minimal/src/main.rs b/examples/template_minimal/src/main.rs index d7ecd4c..4e1dc7e 100644 --- a/examples/template_minimal/src/main.rs +++ b/examples/template_minimal/src/main.rs @@ -6,25 +6,25 @@ use libretrogd::system::*; use libretrogd::utils::rnd_value; fn main() -> Result<()> { - let mut system = SystemBuilder::new().window_title("Minimal Template").vsync(true).build()?; + let mut system = SystemBuilder::new().window_title("Minimal Template").vsync(true).build()?; - let font = BitmaskFont::new_vga_font()?; + let font = BitmaskFont::new_vga_font()?; - system.video.clear(0); - system.video.print_string("Hello, world!", 20, 20, FontRenderOpts::Color(15), &font); + system.video.clear(0); + system.video.print_string("Hello, world!", 20, 20, FontRenderOpts::Color(15), &font); - while !system.do_events() { - if system.input_devices.keyboard.is_key_pressed(Scancode::Escape) { - break; - } + while !system.do_events() { + if system.input_devices.keyboard.is_key_pressed(Scancode::Escape) { + break; + } - let x = rnd_value(0, SCREEN_RIGHT) as i32; - let y = rnd_value(0, SCREEN_BOTTOM) as i32; - let color = rnd_value(0, 255); - system.video.set_pixel(x, y, color); + let x = rnd_value(0, SCREEN_RIGHT) as i32; + let y = rnd_value(0, SCREEN_BOTTOM) as i32; + let color = rnd_value(0, 255); + system.video.set_pixel(x, y, color); - system.display()?; - } + system.display()?; + } - Ok(()) + Ok(()) } diff --git a/libretrogd/benches/bitmap.rs b/libretrogd/benches/bitmap.rs index c6dae24..255d851 100644 --- a/libretrogd/benches/bitmap.rs +++ b/libretrogd/benches/bitmap.rs @@ -1,26 +1,26 @@ -use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use criterion::{black_box, Criterion, criterion_group, criterion_main}; -use libretrogd::graphics::*; use libretrogd::{SCREEN_HEIGHT, SCREEN_WIDTH}; +use libretrogd::graphics::*; pub fn criterion_benchmark(c: &mut Criterion) { - let mut source = Bitmap::new(SCREEN_WIDTH, SCREEN_HEIGHT).unwrap(); - let mut dest = vec![0u32; (SCREEN_WIDTH * SCREEN_HEIGHT * 4) as usize].into_boxed_slice(); - let palette = Palette::new_vga_palette().unwrap(); + let mut source = Bitmap::new(SCREEN_WIDTH, SCREEN_HEIGHT).unwrap(); + let mut dest = vec![0u32; (SCREEN_WIDTH * SCREEN_HEIGHT * 4) as usize].into_boxed_slice(); + let palette = Palette::new_vga_palette().unwrap(); - c.bench_function("deindex_bitmap_pixels", |b| { - b.iter(|| source.copy_as_argb_to(&mut dest, &palette)) - }); + c.bench_function("deindex_bitmap_pixels", |b| { + b.iter(|| source.copy_as_argb_to(&mut dest, &palette)) + }); - c.bench_function("set_pixel", |b| { - b.iter(|| source.set_pixel(black_box(100), black_box(100), black_box(42))) - }); + c.bench_function("set_pixel", |b| { + b.iter(|| source.set_pixel(black_box(100), black_box(100), black_box(42))) + }); - c.bench_function("set_pixel_unchecked", |b| { - b.iter(|| unsafe { - source.set_pixel_unchecked(black_box(100), black_box(100), black_box(42)) - }) - }); + c.bench_function("set_pixel_unchecked", |b| { + b.iter(|| unsafe { + source.set_pixel_unchecked(black_box(100), black_box(100), black_box(42)) + }) + }); } criterion_group!(benches, criterion_benchmark); diff --git a/libretrogd/benches/blit.rs b/libretrogd/benches/blit.rs index 4024935..afee84a 100644 --- a/libretrogd/benches/blit.rs +++ b/libretrogd/benches/blit.rs @@ -1,498 +1,497 @@ use std::path::Path; -use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use criterion::{black_box, Criterion, criterion_group, criterion_main}; use libretrogd::graphics::*; use libretrogd::math::*; pub fn criterion_benchmark(c: &mut Criterion) { - let mut framebuffer = Bitmap::new(320, 240).unwrap(); - let (bmp, _) = Bitmap::load_iff_file(Path::new("./test-assets/test-tiles.lbm")).unwrap(); + let mut framebuffer = Bitmap::new(320, 240).unwrap(); + let (bmp, _) = Bitmap::load_iff_file(Path::new("./test-assets/test-tiles.lbm")).unwrap(); - let mut solid_bmp = Bitmap::new(16, 16).unwrap(); - solid_bmp.blit_region(BlitMethod::Solid, &bmp, &Rect::new(16, 16, 16, 16), 0, 0); - let mut trans_bmp = Bitmap::new(16, 16).unwrap(); - trans_bmp.blit_region(BlitMethod::Solid, &bmp, &Rect::new(160, 0, 16, 16), 0, 0); + let mut solid_bmp = Bitmap::new(16, 16).unwrap(); + solid_bmp.blit_region(BlitMethod::Solid, &bmp, &Rect::new(16, 16, 16, 16), 0, 0); + let mut trans_bmp = Bitmap::new(16, 16).unwrap(); + trans_bmp.blit_region(BlitMethod::Solid, &bmp, &Rect::new(160, 0, 16, 16), 0, 0); - ////// + ////// - c.bench_function("blit_single_checked_solid", |b| { - b.iter(|| { - framebuffer.blit( - black_box(BlitMethod::Solid), - black_box(&solid_bmp), - black_box(100), - black_box(100), - ) - }) - }); + c.bench_function("blit_single_checked_solid", |b| { + b.iter(|| { + framebuffer.blit( + black_box(BlitMethod::Solid), + black_box(&solid_bmp), + black_box(100), + black_box(100), + ) + }) + }); - c.bench_function("blit_single_unchecked_solid", |b| { - b.iter(|| unsafe { - framebuffer.blit_unchecked( - black_box(BlitMethod::Solid), - black_box(&solid_bmp), - black_box(100), - black_box(100), - ) - }) - }); + c.bench_function("blit_single_unchecked_solid", |b| { + b.iter(|| unsafe { + framebuffer.blit_unchecked( + black_box(BlitMethod::Solid), + black_box(&solid_bmp), + black_box(100), + black_box(100), + ) + }) + }); - ////// + ////// - c.bench_function("blit_single_checked_transparent", |b| { - b.iter(|| { - framebuffer.blit( - black_box(BlitMethod::Transparent(0)), - black_box(&trans_bmp), - black_box(100), - black_box(100), - ) - }) - }); + c.bench_function("blit_single_checked_transparent", |b| { + b.iter(|| { + framebuffer.blit( + black_box(BlitMethod::Transparent(0)), + black_box(&trans_bmp), + black_box(100), + black_box(100), + ) + }) + }); - c.bench_function("blit_single_unchecked_transparent", |b| { - b.iter(|| unsafe { - framebuffer.blit_unchecked( - black_box(BlitMethod::Transparent(0)), - black_box(&trans_bmp), - black_box(100), - black_box(100), - ) - }) - }); + c.bench_function("blit_single_unchecked_transparent", |b| { + b.iter(|| unsafe { + framebuffer.blit_unchecked( + black_box(BlitMethod::Transparent(0)), + black_box(&trans_bmp), + black_box(100), + black_box(100), + ) + }) + }); - ////// + ////// - c.bench_function("blit_solid_flipped_not_flipped", |b| { - b.iter(|| { - framebuffer.blit( - black_box(BlitMethod::SolidFlipped { - horizontal_flip: false, - vertical_flip: false - }), - black_box(&solid_bmp), - black_box(100), - black_box(100), - ) - }) - }); + c.bench_function("blit_solid_flipped_not_flipped", |b| { + b.iter(|| { + framebuffer.blit( + black_box(BlitMethod::SolidFlipped { + horizontal_flip: false, + vertical_flip: false, + }), + black_box(&solid_bmp), + black_box(100), + black_box(100), + ) + }) + }); - c.bench_function("blit_solid_flipped_horizontally", |b| { - b.iter(|| { - framebuffer.blit( - black_box(BlitMethod::SolidFlipped { - horizontal_flip: true, - vertical_flip: false - }), - black_box(&solid_bmp), - black_box(100), - black_box(100), - ) - }) - }); + c.bench_function("blit_solid_flipped_horizontally", |b| { + b.iter(|| { + framebuffer.blit( + black_box(BlitMethod::SolidFlipped { + horizontal_flip: true, + vertical_flip: false, + }), + black_box(&solid_bmp), + black_box(100), + black_box(100), + ) + }) + }); - c.bench_function("blit_solid_flipped_vertically", |b| { - b.iter(|| { - framebuffer.blit( - black_box(BlitMethod::SolidFlipped { - horizontal_flip: false, - vertical_flip: true - }), - black_box(&solid_bmp), - black_box(100), - black_box(100), - ) - }) - }); + c.bench_function("blit_solid_flipped_vertically", |b| { + b.iter(|| { + framebuffer.blit( + black_box(BlitMethod::SolidFlipped { + horizontal_flip: false, + vertical_flip: true, + }), + black_box(&solid_bmp), + black_box(100), + black_box(100), + ) + }) + }); - c.bench_function("blit_solid_flipped_horizontally_and_vertically", |b| { - b.iter(|| { - framebuffer.blit( - black_box(BlitMethod::SolidFlipped { - horizontal_flip: true, - vertical_flip: true - }), - black_box(&solid_bmp), - black_box(100), - black_box(100), - ) - }) - }); + c.bench_function("blit_solid_flipped_horizontally_and_vertically", |b| { + b.iter(|| { + framebuffer.blit( + black_box(BlitMethod::SolidFlipped { + horizontal_flip: true, + vertical_flip: true, + }), + black_box(&solid_bmp), + black_box(100), + black_box(100), + ) + }) + }); - ////// + ////// - c.bench_function("blit_transparent_flipped_not_flipped", |b| { - b.iter(|| { - framebuffer.blit( - black_box(BlitMethod::TransparentFlipped { - transparent_color: 0, - horizontal_flip: false, - vertical_flip: false - }), - black_box(&trans_bmp), - black_box(100), - black_box(100), - ) - }) - }); + c.bench_function("blit_transparent_flipped_not_flipped", |b| { + b.iter(|| { + framebuffer.blit( + black_box(BlitMethod::TransparentFlipped { + transparent_color: 0, + horizontal_flip: false, + vertical_flip: false, + }), + black_box(&trans_bmp), + black_box(100), + black_box(100), + ) + }) + }); - c.bench_function("blit_transparent_flipped_horizontally", |b| { - b.iter(|| { - framebuffer.blit( - black_box(BlitMethod::TransparentFlipped { - transparent_color: 0, - horizontal_flip: true, - vertical_flip: false - }), - black_box(&trans_bmp), - black_box(100), - black_box(100), - ) - }) - }); + c.bench_function("blit_transparent_flipped_horizontally", |b| { + b.iter(|| { + framebuffer.blit( + black_box(BlitMethod::TransparentFlipped { + transparent_color: 0, + horizontal_flip: true, + vertical_flip: false, + }), + black_box(&trans_bmp), + black_box(100), + black_box(100), + ) + }) + }); - c.bench_function("blit_transparent_flipped_vertically", |b| { - b.iter(|| { - framebuffer.blit( - black_box(BlitMethod::TransparentFlipped { - transparent_color: 0, - horizontal_flip: false, - vertical_flip: true - }), - black_box(&trans_bmp), - black_box(100), - black_box(100), - ) - }) - }); + c.bench_function("blit_transparent_flipped_vertically", |b| { + b.iter(|| { + framebuffer.blit( + black_box(BlitMethod::TransparentFlipped { + transparent_color: 0, + horizontal_flip: false, + vertical_flip: true, + }), + black_box(&trans_bmp), + black_box(100), + black_box(100), + ) + }) + }); - c.bench_function("blit_transparent_flipped_horizontally_and_vertically", |b| { - b.iter(|| { - framebuffer.blit( - black_box(BlitMethod::TransparentFlipped { - transparent_color: 0, - horizontal_flip: true, - vertical_flip: true - }), - black_box(&trans_bmp), - black_box(100), - black_box(100), - ) - }) - }); + c.bench_function("blit_transparent_flipped_horizontally_and_vertically", |b| { + b.iter(|| { + framebuffer.blit( + black_box(BlitMethod::TransparentFlipped { + transparent_color: 0, + horizontal_flip: true, + vertical_flip: true, + }), + black_box(&trans_bmp), + black_box(100), + black_box(100), + ) + }) + }); - ////// + ////// - c.bench_function("blit_transparent_single_flipped_not_flipped", |b| { - b.iter(|| { - framebuffer.blit( - black_box(BlitMethod::TransparentFlippedSingle { - transparent_color: 0, - horizontal_flip: false, - vertical_flip: false, - draw_color: 17, - }), - black_box(&trans_bmp), - black_box(100), - black_box(100), - ) - }) - }); + c.bench_function("blit_transparent_single_flipped_not_flipped", |b| { + b.iter(|| { + framebuffer.blit( + black_box(BlitMethod::TransparentFlippedSingle { + transparent_color: 0, + horizontal_flip: false, + vertical_flip: false, + draw_color: 17, + }), + black_box(&trans_bmp), + black_box(100), + black_box(100), + ) + }) + }); - c.bench_function("blit_transparent_single_flipped_horizontally", |b| { - b.iter(|| { - framebuffer.blit( - black_box(BlitMethod::TransparentFlippedSingle { - transparent_color: 0, - horizontal_flip: true, - vertical_flip: false, - draw_color: 17, - }), - black_box(&trans_bmp), - black_box(100), - black_box(100), - ) - }) - }); + c.bench_function("blit_transparent_single_flipped_horizontally", |b| { + b.iter(|| { + framebuffer.blit( + black_box(BlitMethod::TransparentFlippedSingle { + transparent_color: 0, + horizontal_flip: true, + vertical_flip: false, + draw_color: 17, + }), + black_box(&trans_bmp), + black_box(100), + black_box(100), + ) + }) + }); - c.bench_function("blit_transparent_single_flipped_vertically", |b| { - b.iter(|| { - framebuffer.blit( - black_box(BlitMethod::TransparentFlippedSingle { - transparent_color: 0, - horizontal_flip: false, - vertical_flip: true, - draw_color: 17, - }), - black_box(&trans_bmp), - black_box(100), - black_box(100), - ) - }) - }); + c.bench_function("blit_transparent_single_flipped_vertically", |b| { + b.iter(|| { + framebuffer.blit( + black_box(BlitMethod::TransparentFlippedSingle { + transparent_color: 0, + horizontal_flip: false, + vertical_flip: true, + draw_color: 17, + }), + black_box(&trans_bmp), + black_box(100), + black_box(100), + ) + }) + }); - c.bench_function("blit_transparent_single_flipped_horizontally_and_vertically", |b| { - b.iter(|| { - framebuffer.blit( - black_box(BlitMethod::TransparentFlippedSingle { - transparent_color: 0, - horizontal_flip: true, - vertical_flip: true, - draw_color: 17, - }), - black_box(&trans_bmp), - black_box(100), - black_box(100), - ) - }) - }); + c.bench_function("blit_transparent_single_flipped_horizontally_and_vertically", |b| { + b.iter(|| { + framebuffer.blit( + black_box(BlitMethod::TransparentFlippedSingle { + transparent_color: 0, + horizontal_flip: true, + vertical_flip: true, + draw_color: 17, + }), + black_box(&trans_bmp), + black_box(100), + black_box(100), + ) + }) + }); - ////// + ////// - c.bench_function("blit_transparent_single", |b| { - b.iter(|| { - framebuffer.blit( - black_box(BlitMethod::TransparentSingle { - transparent_color: 0, - draw_color: 17, - }), - black_box(&trans_bmp), - black_box(100), - black_box(100), - ) - }) - }); + c.bench_function("blit_transparent_single", |b| { + b.iter(|| { + framebuffer.blit( + black_box(BlitMethod::TransparentSingle { + transparent_color: 0, + draw_color: 17, + }), + black_box(&trans_bmp), + black_box(100), + black_box(100), + ) + }) + }); - ////// + ////// - c.bench_function("blit_transparent_offset", |b| { - b.iter(|| { - framebuffer.blit( - black_box(BlitMethod::TransparentOffset { - transparent_color: 0, - offset: 42, - }), - black_box(&trans_bmp), - black_box(100), - black_box(100), - ) - }) - }); + c.bench_function("blit_transparent_offset", |b| { + b.iter(|| { + framebuffer.blit( + black_box(BlitMethod::TransparentOffset { + transparent_color: 0, + offset: 42, + }), + black_box(&trans_bmp), + black_box(100), + black_box(100), + ) + }) + }); - ////// + ////// - c.bench_function("blit_transparent_offset_flipped_not_flipped", |b| { - b.iter(|| { - framebuffer.blit( - black_box(BlitMethod::TransparentFlippedOffset { - transparent_color: 0, - horizontal_flip: false, - vertical_flip: false, - offset: 42, - }), - black_box(&trans_bmp), - black_box(100), - black_box(100), - ) - }) - }); + c.bench_function("blit_transparent_offset_flipped_not_flipped", |b| { + b.iter(|| { + framebuffer.blit( + black_box(BlitMethod::TransparentFlippedOffset { + transparent_color: 0, + horizontal_flip: false, + vertical_flip: false, + offset: 42, + }), + black_box(&trans_bmp), + black_box(100), + black_box(100), + ) + }) + }); - c.bench_function("blit_transparent_offset_flipped_horizontally", |b| { - b.iter(|| { - framebuffer.blit( - black_box(BlitMethod::TransparentFlippedOffset { - transparent_color: 0, - horizontal_flip: true, - vertical_flip: false, - offset: 42, - }), - black_box(&trans_bmp), - black_box(100), - black_box(100), - ) - }) - }); + c.bench_function("blit_transparent_offset_flipped_horizontally", |b| { + b.iter(|| { + framebuffer.blit( + black_box(BlitMethod::TransparentFlippedOffset { + transparent_color: 0, + horizontal_flip: true, + vertical_flip: false, + offset: 42, + }), + black_box(&trans_bmp), + black_box(100), + black_box(100), + ) + }) + }); - c.bench_function("blit_transparent_offset_flipped_vertically", |b| { - b.iter(|| { - framebuffer.blit( - black_box(BlitMethod::TransparentFlippedOffset { - transparent_color: 0, - horizontal_flip: false, - vertical_flip: true, - offset: 42, - }), - black_box(&trans_bmp), - black_box(100), - black_box(100), - ) - }) - }); + c.bench_function("blit_transparent_offset_flipped_vertically", |b| { + b.iter(|| { + framebuffer.blit( + black_box(BlitMethod::TransparentFlippedOffset { + transparent_color: 0, + horizontal_flip: false, + vertical_flip: true, + offset: 42, + }), + black_box(&trans_bmp), + black_box(100), + black_box(100), + ) + }) + }); - c.bench_function("blit_transparent_offset_flipped_horizontally_and_vertically", |b| { - b.iter(|| { - framebuffer.blit( - black_box(BlitMethod::TransparentFlippedOffset { - transparent_color: 0, - horizontal_flip: true, - vertical_flip: true, - offset: 42, - }), - black_box(&trans_bmp), - black_box(100), - black_box(100), - ) - }) - }); + c.bench_function("blit_transparent_offset_flipped_horizontally_and_vertically", |b| { + b.iter(|| { + framebuffer.blit( + black_box(BlitMethod::TransparentFlippedOffset { + transparent_color: 0, + horizontal_flip: true, + vertical_flip: true, + offset: 42, + }), + black_box(&trans_bmp), + black_box(100), + black_box(100), + ) + }) + }); - ////// + ////// - c.bench_function("blit_solid_offset", |b| { - b.iter(|| { - framebuffer.blit( - black_box(BlitMethod::SolidOffset(42)), - black_box(&solid_bmp), - black_box(100), - black_box(100), - ) - }) - }); + c.bench_function("blit_solid_offset", |b| { + b.iter(|| { + framebuffer.blit( + black_box(BlitMethod::SolidOffset(42)), + black_box(&solid_bmp), + black_box(100), + black_box(100), + ) + }) + }); - ////// + ////// - c.bench_function("blit_solid_offset_flipped_not_flipped", |b| { - b.iter(|| { - framebuffer.blit( - black_box(BlitMethod::SolidFlippedOffset { - horizontal_flip: false, - vertical_flip: false, - offset: 42, - }), - black_box(&solid_bmp), - black_box(100), - black_box(100), - ) - }) - }); + c.bench_function("blit_solid_offset_flipped_not_flipped", |b| { + b.iter(|| { + framebuffer.blit( + black_box(BlitMethod::SolidFlippedOffset { + horizontal_flip: false, + vertical_flip: false, + offset: 42, + }), + black_box(&solid_bmp), + black_box(100), + black_box(100), + ) + }) + }); - c.bench_function("blit_solid_offset_flipped_horizontally", |b| { - b.iter(|| { - framebuffer.blit( - black_box(BlitMethod::SolidFlippedOffset { - horizontal_flip: true, - vertical_flip: false, - offset: 42, - }), - black_box(&solid_bmp), - black_box(100), - black_box(100), - ) - }) - }); + c.bench_function("blit_solid_offset_flipped_horizontally", |b| { + b.iter(|| { + framebuffer.blit( + black_box(BlitMethod::SolidFlippedOffset { + horizontal_flip: true, + vertical_flip: false, + offset: 42, + }), + black_box(&solid_bmp), + black_box(100), + black_box(100), + ) + }) + }); - c.bench_function("blit_solid_offset_flipped_vertically", |b| { - b.iter(|| { - framebuffer.blit( - black_box(BlitMethod::SolidFlippedOffset { - horizontal_flip: false, - vertical_flip: true, - offset: 42, - }), - black_box(&solid_bmp), - black_box(100), - black_box(100), - ) - }) - }); + c.bench_function("blit_solid_offset_flipped_vertically", |b| { + b.iter(|| { + framebuffer.blit( + black_box(BlitMethod::SolidFlippedOffset { + horizontal_flip: false, + vertical_flip: true, + offset: 42, + }), + black_box(&solid_bmp), + black_box(100), + black_box(100), + ) + }) + }); - c.bench_function("blit_solid_offset_flipped_horizontally_and_vertically", |b| { - b.iter(|| { - framebuffer.blit( - black_box(BlitMethod::SolidFlippedOffset { - horizontal_flip: true, - vertical_flip: true, - offset: 42, - }), - black_box(&solid_bmp), - black_box(100), - black_box(100), - ) - }) - }); + c.bench_function("blit_solid_offset_flipped_horizontally_and_vertically", |b| { + b.iter(|| { + framebuffer.blit( + black_box(BlitMethod::SolidFlippedOffset { + horizontal_flip: true, + vertical_flip: true, + offset: 42, + }), + black_box(&solid_bmp), + black_box(100), + black_box(100), + ) + }) + }); - ////// + ////// - c.bench_function("blit_rotozoom", |b| { - b.iter(|| { - framebuffer.blit( - black_box(BlitMethod::RotoZoom { - angle: 73.0f32.to_radians(), - scale_x: 1.2, - scale_y: 0.8, - }), - black_box(&solid_bmp), - black_box(100), - black_box(100), - ) - }) - }); + c.bench_function("blit_rotozoom", |b| { + b.iter(|| { + framebuffer.blit( + black_box(BlitMethod::RotoZoom { + angle: 73.0f32.to_radians(), + scale_x: 1.2, + scale_y: 0.8, + }), + black_box(&solid_bmp), + black_box(100), + black_box(100), + ) + }) + }); - ////// + ////// - c.bench_function("blit_rotozoom_offset", |b| { - b.iter(|| { - framebuffer.blit( - black_box(BlitMethod::RotoZoomOffset { - angle: 73.0f32.to_radians(), - scale_x: 1.2, - scale_y: 0.8, - offset: 42, - }), - black_box(&solid_bmp), - black_box(100), - black_box(100), - ) - }) - }); + c.bench_function("blit_rotozoom_offset", |b| { + b.iter(|| { + framebuffer.blit( + black_box(BlitMethod::RotoZoomOffset { + angle: 73.0f32.to_radians(), + scale_x: 1.2, + scale_y: 0.8, + offset: 42, + }), + black_box(&solid_bmp), + black_box(100), + black_box(100), + ) + }) + }); - ////// + ////// - c.bench_function("blit_rotozoom_transparent", |b| { - b.iter(|| { - framebuffer.blit( - black_box(BlitMethod::RotoZoomTransparent { - angle: 73.0f32.to_radians(), - scale_x: 1.2, - scale_y: 0.8, - transparent_color: 0, - }), - black_box(&trans_bmp), - black_box(100), - black_box(100), - ) - }) - }); + c.bench_function("blit_rotozoom_transparent", |b| { + b.iter(|| { + framebuffer.blit( + black_box(BlitMethod::RotoZoomTransparent { + angle: 73.0f32.to_radians(), + scale_x: 1.2, + scale_y: 0.8, + transparent_color: 0, + }), + black_box(&trans_bmp), + black_box(100), + black_box(100), + ) + }) + }); - ////// - - c.bench_function("blit_rotozoom_offset_transparent", |b| { - b.iter(|| { - framebuffer.blit( - black_box(BlitMethod::RotoZoomTransparentOffset { - angle: 73.0f32.to_radians(), - scale_x: 1.2, - scale_y: 0.8, - transparent_color: 0, - offset: 42, - }), - black_box(&trans_bmp), - black_box(100), - black_box(100), - ) - }) - }); + ////// + c.bench_function("blit_rotozoom_offset_transparent", |b| { + b.iter(|| { + framebuffer.blit( + black_box(BlitMethod::RotoZoomTransparentOffset { + angle: 73.0f32.to_radians(), + scale_x: 1.2, + scale_y: 0.8, + transparent_color: 0, + offset: 42, + }), + black_box(&trans_bmp), + black_box(100), + black_box(100), + ) + }) + }); } criterion_group!(benches, criterion_benchmark); diff --git a/libretrogd/benches/loading.rs b/libretrogd/benches/loading.rs index 61812f7..132dde3 100644 --- a/libretrogd/benches/loading.rs +++ b/libretrogd/benches/loading.rs @@ -8,19 +8,19 @@ pub static SMALL_GIF_FILE_BYTES: &[u8] = include_bytes!("../test-assets/test.gif pub static LARGE_GIF_FILE_BYTES: &[u8] = include_bytes!("../test-assets/test_image.gif"); pub fn criterion_benchmark(c: &mut Criterion) { - c.bench_function("loading_small_gif", |b| { - b.iter(|| { - let mut reader = Cursor::new(SMALL_GIF_FILE_BYTES); - Bitmap::load_gif_bytes(black_box(&mut reader)).unwrap(); - }) - }); + c.bench_function("loading_small_gif", |b| { + b.iter(|| { + let mut reader = Cursor::new(SMALL_GIF_FILE_BYTES); + Bitmap::load_gif_bytes(black_box(&mut reader)).unwrap(); + }) + }); - c.bench_function("loading_large_gif", |b| { - b.iter(|| { - let mut reader = Cursor::new(LARGE_GIF_FILE_BYTES); - Bitmap::load_gif_bytes(black_box(&mut reader)).unwrap(); - }) - }); + c.bench_function("loading_large_gif", |b| { + b.iter(|| { + let mut reader = Cursor::new(LARGE_GIF_FILE_BYTES); + Bitmap::load_gif_bytes(black_box(&mut reader)).unwrap(); + }) + }); } criterion_group!(benches, criterion_benchmark); diff --git a/libretrogd/src/audio/buffer/mod.rs b/libretrogd/src/audio/buffer/mod.rs index 12d643e..ce6944e 100644 --- a/libretrogd/src/audio/buffer/mod.rs +++ b/libretrogd/src/audio/buffer/mod.rs @@ -6,64 +6,64 @@ pub mod wav; #[derive(Error, Debug)] pub enum AudioBufferError { - #[error("Error during format conversion: {0}")] - ConversionError(String), + #[error("Error during format conversion: {0}")] + ConversionError(String), } /// Holds audio sample data that can be played via [`AudioDevice`]. #[derive(Clone, Eq, PartialEq)] pub struct AudioBuffer { - spec: AudioSpec, - pub data: Vec, + spec: AudioSpec, + pub data: Vec, } impl std::fmt::Debug for AudioBuffer { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("AudioBuffer") - .field("spec", &self.spec) - .field("data.len()", &self.data.len()) - .finish_non_exhaustive() - } + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("AudioBuffer") + .field("spec", &self.spec) + .field("data.len()", &self.data.len()) + .finish_non_exhaustive() + } } impl AudioBuffer { - /// Creates and returns a new, empty, [`AudioBuffer`] that will hold audio sample data in the - /// spec/format given. - pub fn new(spec: AudioSpec) -> Self { - AudioBuffer { - spec, - data: Vec::new(), - } - } + /// Creates and returns a new, empty, [`AudioBuffer`] that will hold audio sample data in the + /// spec/format given. + pub fn new(spec: AudioSpec) -> Self { + AudioBuffer { + spec, + data: Vec::new(), + } + } - /// Returns the spec of the audio sample data that this buffer contains. - #[inline] - pub fn spec(&self) -> &AudioSpec { - &self.spec - } + /// Returns the spec of the audio sample data that this buffer contains. + #[inline] + pub fn spec(&self) -> &AudioSpec { + &self.spec + } - /// Converts the audio sample data in this buffer to the spec given, returning the newly - /// converted buffer. - pub fn convert(self, to_spec: &AudioSpec) -> Result { - if self.spec == *to_spec { - Ok(self) - } else { - let converter = sdl2::audio::AudioCVT::new( - self.spec.format(), - self.spec.channels(), - self.spec.frequency() as i32, - to_spec.format(), - to_spec.channels(), - to_spec.frequency() as i32, - ); - match converter { - Ok(converter) => { - let mut result = AudioBuffer::new(*to_spec); - result.data = converter.convert(self.data); - Ok(result) - } - Err(string) => Err(AudioBufferError::ConversionError(string)), - } - } - } + /// Converts the audio sample data in this buffer to the spec given, returning the newly + /// converted buffer. + pub fn convert(self, to_spec: &AudioSpec) -> Result { + if self.spec == *to_spec { + Ok(self) + } else { + let converter = sdl2::audio::AudioCVT::new( + self.spec.format(), + self.spec.channels(), + self.spec.frequency() as i32, + to_spec.format(), + to_spec.channels(), + to_spec.frequency() as i32, + ); + match converter { + Ok(converter) => { + let mut result = AudioBuffer::new(*to_spec); + result.data = converter.convert(self.data); + Ok(result) + } + Err(string) => Err(AudioBufferError::ConversionError(string)), + } + } + } } diff --git a/libretrogd/src/audio/buffer/wav.rs b/libretrogd/src/audio/buffer/wav.rs index 9af9718..16dbeea 100644 --- a/libretrogd/src/audio/buffer/wav.rs +++ b/libretrogd/src/audio/buffer/wav.rs @@ -12,322 +12,322 @@ use crate::utils::io::StreamSize; #[derive(Error, Debug)] pub enum WavError { - #[error("Bad or unsupported WAV file: {0}")] - BadFile(String), + #[error("Bad or unsupported WAV file: {0}")] + BadFile(String), - #[error("WAV I/O error")] - IOError(#[from] std::io::Error), + #[error("WAV I/O error")] + IOError(#[from] std::io::Error), } #[derive(Debug, Copy, Clone)] struct ChunkId { - id: [u8; 4], + id: [u8; 4], } impl ChunkId { - pub fn read(reader: &mut T) -> Result { - let mut id = [0u8; 4]; - reader.read_exact(&mut id)?; - Ok(ChunkId { id }) - } + pub fn read(reader: &mut T) -> Result { + let mut id = [0u8; 4]; + reader.read_exact(&mut id)?; + Ok(ChunkId { id }) + } - #[allow(dead_code)] - pub fn write(&self, writer: &mut T) -> Result<(), WavError> { - writer.write_all(&self.id)?; - Ok(()) - } + #[allow(dead_code)] + pub fn write(&self, writer: &mut T) -> Result<(), WavError> { + writer.write_all(&self.id)?; + Ok(()) + } } #[derive(Debug, Copy, Clone)] struct SubChunkHeader { - chunk_id: ChunkId, - size: u32, + chunk_id: ChunkId, + size: u32, } impl SubChunkHeader { - pub fn read(reader: &mut T) -> Result { - let chunk_id = ChunkId::read(reader)?; - let size = reader.read_u32::()?; - Ok(SubChunkHeader { chunk_id, size }) - } + pub fn read(reader: &mut T) -> Result { + let chunk_id = ChunkId::read(reader)?; + let size = reader.read_u32::()?; + Ok(SubChunkHeader { chunk_id, size }) + } - #[allow(dead_code)] - pub fn write(&self, writer: &mut T) -> Result<(), WavError> { - self.chunk_id.write(writer)?; - writer.write_u32::(self.size)?; - Ok(()) - } + #[allow(dead_code)] + pub fn write(&self, writer: &mut T) -> Result<(), WavError> { + self.chunk_id.write(writer)?; + writer.write_u32::(self.size)?; + Ok(()) + } } #[derive(Debug, Copy, Clone)] struct WavHeader { - file_chunk: SubChunkHeader, - file_container_id: ChunkId, + file_chunk: SubChunkHeader, + file_container_id: ChunkId, } impl WavHeader { - pub fn read(reader: &mut T) -> Result { - let file_chunk = SubChunkHeader::read(reader)?; - let file_container_id = ChunkId::read(reader)?; - Ok(WavHeader { - file_chunk, - file_container_id, - }) - } + pub fn read(reader: &mut T) -> Result { + let file_chunk = SubChunkHeader::read(reader)?; + let file_container_id = ChunkId::read(reader)?; + Ok(WavHeader { + file_chunk, + file_container_id, + }) + } - #[allow(dead_code)] - pub fn write(&self, writer: &mut T) -> Result<(), WavError> { - self.file_chunk.write(writer)?; - self.file_container_id.write(writer)?; - Ok(()) - } + #[allow(dead_code)] + pub fn write(&self, writer: &mut T) -> Result<(), WavError> { + self.file_chunk.write(writer)?; + self.file_container_id.write(writer)?; + Ok(()) + } } #[derive(Debug, Clone)] #[allow(dead_code)] struct FormatChunk { - compression_code: u16, - channels: u16, - frequency: u32, - bytes_per_second: u32, - block_alignment: u16, - bits_per_sample: u16, - additional_data_length: u16, - additional_data: Option>, + compression_code: u16, + channels: u16, + frequency: u32, + bytes_per_second: u32, + block_alignment: u16, + bits_per_sample: u16, + additional_data_length: u16, + additional_data: Option>, } impl FormatChunk { - pub fn read( - reader: &mut T, - chunk_header: &SubChunkHeader, - ) -> Result { - let compression_code = reader.read_u16::()?; - let channels = reader.read_u16::()?; - let frequency = reader.read_u32::()?; - let bytes_per_second = reader.read_u32::()?; - let block_alignment = reader.read_u16::()?; - let bits_per_sample = reader.read_u16::()?; - let additional_data_length; - let additional_data; - if chunk_header.size > 16 { - additional_data_length = reader.read_u16::()?; - let mut buffer = vec![0u8; additional_data_length as usize]; - reader.read(&mut buffer)?; - additional_data = Some(buffer.into_boxed_slice()); - } else { - additional_data_length = 0; - additional_data = None; - } + pub fn read( + reader: &mut T, + chunk_header: &SubChunkHeader, + ) -> Result { + let compression_code = reader.read_u16::()?; + let channels = reader.read_u16::()?; + let frequency = reader.read_u32::()?; + let bytes_per_second = reader.read_u32::()?; + let block_alignment = reader.read_u16::()?; + let bits_per_sample = reader.read_u16::()?; + let additional_data_length; + let additional_data; + if chunk_header.size > 16 { + additional_data_length = reader.read_u16::()?; + let mut buffer = vec![0u8; additional_data_length as usize]; + reader.read(&mut buffer)?; + additional_data = Some(buffer.into_boxed_slice()); + } else { + additional_data_length = 0; + additional_data = None; + } - Ok(FormatChunk { - compression_code, - channels, - frequency, - bytes_per_second, - block_alignment, - bits_per_sample, - additional_data_length, - additional_data, - }) - } + Ok(FormatChunk { + compression_code, + channels, + frequency, + bytes_per_second, + block_alignment, + bits_per_sample, + additional_data_length, + additional_data, + }) + } - #[allow(dead_code)] - pub fn write(&self, writer: &mut T) -> Result<(), WavError> { - writer.write_u16::(self.compression_code)?; - writer.write_u16::(self.channels)?; - writer.write_u32::(self.frequency)?; - writer.write_u32::(self.bytes_per_second)?; - writer.write_u16::(self.block_alignment)?; - writer.write_u16::(self.bits_per_sample)?; - if self.additional_data_length > 0 { - writer.write_u16::(self.additional_data_length)?; - writer.write_all(&self.additional_data.as_ref().unwrap())?; - } - Ok(()) - } + #[allow(dead_code)] + pub fn write(&self, writer: &mut T) -> Result<(), WavError> { + writer.write_u16::(self.compression_code)?; + writer.write_u16::(self.channels)?; + writer.write_u32::(self.frequency)?; + writer.write_u32::(self.bytes_per_second)?; + writer.write_u16::(self.block_alignment)?; + writer.write_u16::(self.bits_per_sample)?; + if self.additional_data_length > 0 { + writer.write_u16::(self.additional_data_length)?; + writer.write_all(&self.additional_data.as_ref().unwrap())?; + } + Ok(()) + } } #[derive(Debug, Clone)] struct DataChunk { - data: Box<[u8]>, + data: Box<[u8]>, } impl DataChunk { - pub fn read( - reader: &mut T, - chunk_header: &SubChunkHeader, - is_probably_naive_file: bool, - ) -> Result { - let mut buffer; - if is_probably_naive_file { - // in this scenario, we have doubts about the chunk size being recorded correctly - // (the tool that created this file may have used a buggy calculation). we assume that - // in this case, this wav file is probably written in a "naive" manner and likely only - // contains "fmt" and "data" chunks with the "data" chunk being at the end of the file. - // if this assumption is correct, then we can just read everything until EOF here as - // the "data" chunk contents and that should be ok (assuming this file isn't corrupt - // anyway). - buffer = Vec::new(); - reader.read_to_end(&mut buffer)?; - } else { - // alternatively, this scenario means we are assuming the file was written out more - // properly and we will assume the chunk size is correct and read that many bytes. - // this is best if there is the possibility that there are more chunks than just "fmt" - // and "data" in this wav file and maybe they are in a different order, etc. - // it is important to note that this seems to be an uncommon case for wav files. most - // wav files seem to be written in a fairly "naive" manner. - buffer = vec![0u8; chunk_header.size as usize]; - reader.read_exact(&mut buffer)?; - } - Ok(DataChunk { - data: buffer.into_boxed_slice(), - }) - } + pub fn read( + reader: &mut T, + chunk_header: &SubChunkHeader, + is_probably_naive_file: bool, + ) -> Result { + let mut buffer; + if is_probably_naive_file { + // in this scenario, we have doubts about the chunk size being recorded correctly + // (the tool that created this file may have used a buggy calculation). we assume that + // in this case, this wav file is probably written in a "naive" manner and likely only + // contains "fmt" and "data" chunks with the "data" chunk being at the end of the file. + // if this assumption is correct, then we can just read everything until EOF here as + // the "data" chunk contents and that should be ok (assuming this file isn't corrupt + // anyway). + buffer = Vec::new(); + reader.read_to_end(&mut buffer)?; + } else { + // alternatively, this scenario means we are assuming the file was written out more + // properly and we will assume the chunk size is correct and read that many bytes. + // this is best if there is the possibility that there are more chunks than just "fmt" + // and "data" in this wav file and maybe they are in a different order, etc. + // it is important to note that this seems to be an uncommon case for wav files. most + // wav files seem to be written in a fairly "naive" manner. + buffer = vec![0u8; chunk_header.size as usize]; + reader.read_exact(&mut buffer)?; + } + Ok(DataChunk { + data: buffer.into_boxed_slice(), + }) + } - #[allow(dead_code)] - pub fn write(&self, writer: &mut T) -> Result<(), WavError> { - writer.write_all(self.data.as_ref())?; - Ok(()) - } + #[allow(dead_code)] + pub fn write(&self, writer: &mut T) -> Result<(), WavError> { + writer.write_all(self.data.as_ref())?; + Ok(()) + } } impl AudioBuffer { - /// Loads the bytes of a WAV file into an [`AudioBuffer`]. The returned buffer will be in its - /// original format and may need to be converted before it can be played. - pub fn load_wav_bytes(reader: &mut T) -> Result { - let file_size = reader.stream_size()?; + /// Loads the bytes of a WAV file into an [`AudioBuffer`]. The returned buffer will be in its + /// original format and may need to be converted before it can be played. + pub fn load_wav_bytes(reader: &mut T) -> Result { + let file_size = reader.stream_size()?; - let header = WavHeader::read(reader)?; - if header.file_chunk.chunk_id.id != *b"RIFF" { - return Err(WavError::BadFile(String::from( - "Unexpected RIFF chunk id, probably not a WAV file", - ))); - } - if header.file_container_id.id != *b"WAVE" { - return Err(WavError::BadFile(String::from( - "Unexpected RIFF container id, probably not a WAV file", - ))); - } + let header = WavHeader::read(reader)?; + if header.file_chunk.chunk_id.id != *b"RIFF" { + return Err(WavError::BadFile(String::from( + "Unexpected RIFF chunk id, probably not a WAV file", + ))); + } + if header.file_container_id.id != *b"WAVE" { + return Err(WavError::BadFile(String::from( + "Unexpected RIFF container id, probably not a WAV file", + ))); + } - // some tools like sfxr and jsfxr incorrectly calculate data sizes, seemingly using a - // hardcoded 16-bit sample size in their calculations even when the file being created has - // 8-bit sample sizes. this means the chunk size here and the "data" chunk size will be - // larger than they should be for the _actual_ data present in the file. - // of course, if the chunk size here is wrong, it could also mean a corrupt file. but we'll - // proceed regardless, with the assumption that an incorrect size here probably means that - // the file was created using these semi-broken tools and should act accordingly later on. - // if the file is actually corrupt (maybe truncated accidentally or something), then we'll - // hit an EOF earlier than expected somewhere too ... - let is_probably_naive_file = file_size - 8 != header.file_chunk.size as u64; + // some tools like sfxr and jsfxr incorrectly calculate data sizes, seemingly using a + // hardcoded 16-bit sample size in their calculations even when the file being created has + // 8-bit sample sizes. this means the chunk size here and the "data" chunk size will be + // larger than they should be for the _actual_ data present in the file. + // of course, if the chunk size here is wrong, it could also mean a corrupt file. but we'll + // proceed regardless, with the assumption that an incorrect size here probably means that + // the file was created using these semi-broken tools and should act accordingly later on. + // if the file is actually corrupt (maybe truncated accidentally or something), then we'll + // hit an EOF earlier than expected somewhere too ... + let is_probably_naive_file = file_size - 8 != header.file_chunk.size as u64; - let mut format: Option = None; - let mut data: Option = None; + let mut format: Option = None; + let mut data: Option = None; - loop { - let chunk_header = match SubChunkHeader::read(reader) { - Ok(header) => header, - Err(WavError::IOError(io_error)) - if io_error.kind() == io::ErrorKind::UnexpectedEof => - { - break; - } - Err(err) => return Err(err), - }; - let chunk_data_position = reader.stream_position()?; + loop { + let chunk_header = match SubChunkHeader::read(reader) { + Ok(header) => header, + Err(WavError::IOError(io_error)) + if io_error.kind() == io::ErrorKind::UnexpectedEof => + { + break; + } + Err(err) => return Err(err), + }; + let chunk_data_position = reader.stream_position()?; - // read only the chunks we recognize / care about - if chunk_header.chunk_id.id == *b"fmt " { - format = Some(FormatChunk::read(reader, &chunk_header)?); - if format.as_ref().unwrap().compression_code != 1 { - return Err(WavError::BadFile(String::from( - "Only PCM format WAV files are supported", - ))); - } - if format.as_ref().unwrap().bits_per_sample != 8 && - format.as_ref().unwrap().bits_per_sample != 16 { - return Err(WavError::BadFile(String::from( - "Only 8-bit and 16-bit sample WAV files are supported", - ))); - } - } else if chunk_header.chunk_id.id == *b"data" { - data = Some(DataChunk::read(reader, &chunk_header, is_probably_naive_file)?); - } + // read only the chunks we recognize / care about + if chunk_header.chunk_id.id == *b"fmt " { + format = Some(FormatChunk::read(reader, &chunk_header)?); + if format.as_ref().unwrap().compression_code != 1 { + return Err(WavError::BadFile(String::from( + "Only PCM format WAV files are supported", + ))); + } + if format.as_ref().unwrap().bits_per_sample != 8 && + format.as_ref().unwrap().bits_per_sample != 16 { + return Err(WavError::BadFile(String::from( + "Only 8-bit and 16-bit sample WAV files are supported", + ))); + } + } else if chunk_header.chunk_id.id == *b"data" { + data = Some(DataChunk::read(reader, &chunk_header, is_probably_naive_file)?); + } - // move to the start of the next chunk (possibly skipping over the current chunk if we - // didn't recognize it above ...) - reader.seek(SeekFrom::Start( - chunk_data_position + chunk_header.size as u64, - ))?; - } + // move to the start of the next chunk (possibly skipping over the current chunk if we + // didn't recognize it above ...) + reader.seek(SeekFrom::Start( + chunk_data_position + chunk_header.size as u64, + ))?; + } - // all done reading the file, now convert the read data into an AudioBuffer ... + // all done reading the file, now convert the read data into an AudioBuffer ... - let mut audio_buffer; + let mut audio_buffer; - if let Some(format) = format { - let sample_format = match format.bits_per_sample { - 8 => AudioFormat::U8, - 16 => AudioFormat::S16LSB, - // this shouldn't be able to happen given the above checks when reading the - // "fmt" chunk - _ => return Err(WavError::BadFile(String::from("Unsupported sample bit size."))) - }; - let spec = AudioSpec::new(format.frequency, format.channels as u8, sample_format); - audio_buffer = AudioBuffer::new(spec); - } else { - return Err(WavError::BadFile(String::from("No 'fmt ' chunk was found"))); - } + if let Some(format) = format { + let sample_format = match format.bits_per_sample { + 8 => AudioFormat::U8, + 16 => AudioFormat::S16LSB, + // this shouldn't be able to happen given the above checks when reading the + // "fmt" chunk + _ => return Err(WavError::BadFile(String::from("Unsupported sample bit size."))) + }; + let spec = AudioSpec::new(format.frequency, format.channels as u8, sample_format); + audio_buffer = AudioBuffer::new(spec); + } else { + return Err(WavError::BadFile(String::from("No 'fmt ' chunk was found"))); + } - if let Some(data) = data { - audio_buffer.data = data.data.into_vec(); - } else { - return Err(WavError::BadFile(String::from("No 'data' chunk was found"))); - } + if let Some(data) = data { + audio_buffer.data = data.data.into_vec(); + } else { + return Err(WavError::BadFile(String::from("No 'data' chunk was found"))); + } - Ok(audio_buffer) - } + Ok(audio_buffer) + } - /// Loads a WAV file into an [`AudioBuffer`]. The returned buffer will be in its original - /// format and may need to be converted before it can be played. - pub fn load_wav_file(path: &Path) -> Result { - let f = File::open(path)?; - let mut reader = BufReader::new(f); - Self::load_wav_bytes(&mut reader) - } + /// Loads a WAV file into an [`AudioBuffer`]. The returned buffer will be in its original + /// format and may need to be converted before it can be played. + pub fn load_wav_file(path: &Path) -> Result { + let f = File::open(path)?; + let mut reader = BufReader::new(f); + Self::load_wav_bytes(&mut reader) + } } #[cfg(test)] mod tests { - use crate::audio::*; + use crate::audio::*; - use super::*; + use super::*; - #[test] - pub fn load_wav_file() -> Result<(), WavError> { - let wav_buffer = AudioBuffer::load_wav_file(Path::new("./test-assets/22khz_8bit_1ch.wav"))?; - assert_eq!(AUDIO_FREQUENCY_22KHZ, wav_buffer.spec().frequency()); - assert_eq!(1, wav_buffer.spec().channels()); - assert_eq!(AudioFormat::U8, wav_buffer.spec.format); - assert_eq!(11248, wav_buffer.data.len()); + #[test] + pub fn load_wav_file() -> Result<(), WavError> { + let wav_buffer = AudioBuffer::load_wav_file(Path::new("./test-assets/22khz_8bit_1ch.wav"))?; + assert_eq!(AUDIO_FREQUENCY_22KHZ, wav_buffer.spec().frequency()); + assert_eq!(1, wav_buffer.spec().channels()); + assert_eq!(AudioFormat::U8, wav_buffer.spec.format); + assert_eq!(11248, wav_buffer.data.len()); - let wav_buffer = AudioBuffer::load_wav_file(Path::new("./test-assets/44khz_8bit_1ch.wav"))?; - assert_eq!(AUDIO_FREQUENCY_44KHZ, wav_buffer.spec().frequency()); - assert_eq!(1, wav_buffer.spec().channels()); - assert_eq!(AudioFormat::U8, wav_buffer.spec.format); - assert_eq!(22496, wav_buffer.data.len()); + let wav_buffer = AudioBuffer::load_wav_file(Path::new("./test-assets/44khz_8bit_1ch.wav"))?; + assert_eq!(AUDIO_FREQUENCY_44KHZ, wav_buffer.spec().frequency()); + assert_eq!(1, wav_buffer.spec().channels()); + assert_eq!(AudioFormat::U8, wav_buffer.spec.format); + assert_eq!(22496, wav_buffer.data.len()); - let wav_buffer = AudioBuffer::load_wav_file(Path::new("./test-assets/22khz_16bit_1ch.wav"))?; - assert_eq!(AUDIO_FREQUENCY_22KHZ, wav_buffer.spec().frequency()); - assert_eq!(1, wav_buffer.spec().channels()); - assert_eq!(AudioFormat::S16LSB, wav_buffer.spec.format); - assert_eq!(22496, wav_buffer.data.len()); + let wav_buffer = AudioBuffer::load_wav_file(Path::new("./test-assets/22khz_16bit_1ch.wav"))?; + assert_eq!(AUDIO_FREQUENCY_22KHZ, wav_buffer.spec().frequency()); + assert_eq!(1, wav_buffer.spec().channels()); + assert_eq!(AudioFormat::S16LSB, wav_buffer.spec.format); + assert_eq!(22496, wav_buffer.data.len()); - let wav_buffer = AudioBuffer::load_wav_file(Path::new("./test-assets/44khz_16bit_1ch.wav"))?; - assert_eq!(AUDIO_FREQUENCY_44KHZ, wav_buffer.spec().frequency()); - assert_eq!(1, wav_buffer.spec().channels()); - assert_eq!(AudioFormat::S16LSB, wav_buffer.spec.format); - assert_eq!(44992, wav_buffer.data.len()); + let wav_buffer = AudioBuffer::load_wav_file(Path::new("./test-assets/44khz_16bit_1ch.wav"))?; + assert_eq!(AUDIO_FREQUENCY_44KHZ, wav_buffer.spec().frequency()); + assert_eq!(1, wav_buffer.spec().channels()); + assert_eq!(AudioFormat::S16LSB, wav_buffer.spec.format); + assert_eq!(44992, wav_buffer.data.len()); - Ok(()) - } + Ok(()) + } } diff --git a/libretrogd/src/audio/device.rs b/libretrogd/src/audio/device.rs index fd6f14c..c5b54de 100644 --- a/libretrogd/src/audio/device.rs +++ b/libretrogd/src/audio/device.rs @@ -8,180 +8,180 @@ use crate::audio::*; /// Represents a "channel" of audio playback that will be mixed together with all of the other /// actively playing audio channels to get the final audio playback. pub struct AudioChannel { - /// Whether the channel is currently playing or not. - pub playing: bool, - /// Whether this channel is playing on a loop or not. If not, once the end of the [`data`] - /// buffer is reached, or the [`AudioGenerator::gen_sample`] method returns `None`, playback - /// on this channel will automatically stop and [`playing`] will be changed to `false`. - pub loops: bool, - /// The audio data buffer (samples) that this channel will play from, **only** if [`generator`] - /// is `None`. - pub data: Vec, - /// An [`AudioGenerator`] instance that will be used to dynamically generate audio data to play - /// on this channel _instead of_ playing from [`data`]. Set this to `None` to play from audio - /// data in [`data`] instead. - pub generator: Option>, - /// The volume level to play this channel at. 1.0 is "normal", 0.0 is completely silent. - pub volume: f32, - /// The current playback position (index). 0 is the start of playback. The end position is - /// either the (current) size of the [`data`] buffer or dependant on the implementation of this - /// channel's current [`generator`] if not `None`. - pub position: usize, + /// Whether the channel is currently playing or not. + pub playing: bool, + /// Whether this channel is playing on a loop or not. If not, once the end of the [`data`] + /// buffer is reached, or the [`AudioGenerator::gen_sample`] method returns `None`, playback + /// on this channel will automatically stop and [`playing`] will be changed to `false`. + pub loops: bool, + /// The audio data buffer (samples) that this channel will play from, **only** if [`generator`] + /// is `None`. + pub data: Vec, + /// An [`AudioGenerator`] instance that will be used to dynamically generate audio data to play + /// on this channel _instead of_ playing from [`data`]. Set this to `None` to play from audio + /// data in [`data`] instead. + pub generator: Option>, + /// The volume level to play this channel at. 1.0 is "normal", 0.0 is completely silent. + pub volume: f32, + /// The current playback position (index). 0 is the start of playback. The end position is + /// either the (current) size of the [`data`] buffer or dependant on the implementation of this + /// channel's current [`generator`] if not `None`. + pub position: usize, } impl std::fmt::Debug for AudioChannel { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("AudioChannel") - .field("playing", &self.playing) - .field("loops", &self.loops) - .field("data.len()", &self.data.len()) - .field("generator", match self.generator { - Some(..) => &"Some(..)", - None => &"None", - }) - .field("volume", &self.volume) - .field("position", &self.position) - .finish_non_exhaustive() - } + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("AudioChannel") + .field("playing", &self.playing) + .field("loops", &self.loops) + .field("data.len()", &self.data.len()) + .field("generator", match self.generator { + Some(..) => &"Some(..)", + None => &"None", + }) + .field("volume", &self.volume) + .field("position", &self.position) + .finish_non_exhaustive() + } } impl AudioChannel { - pub fn new() -> Self { - AudioChannel { - playing: false, - loops: false, - volume: 1.0, - position: 0, - generator: None, - data: Vec::new(), - } - } + pub fn new() -> Self { + AudioChannel { + playing: false, + loops: false, + volume: 1.0, + position: 0, + generator: None, + data: Vec::new(), + } + } - /// Returns the audio sample for the given position, or `None` if that position is invalid. - #[inline] - fn data_at(&mut self, position: usize) -> Option { - if let Some(generator) = &mut self.generator { - generator.gen_sample(position) - } else { - self.data.get(position).copied() - } - } + /// Returns the audio sample for the given position, or `None` if that position is invalid. + #[inline] + fn data_at(&mut self, position: usize) -> Option { + if let Some(generator) = &mut self.generator { + generator.gen_sample(position) + } else { + self.data.get(position).copied() + } + } - /// Returns the next sample from this channel's buffer. If this channel's buffer is done - /// playing or there is no buffer data at all, `None` is returned. If the next sample was - /// successfully loaded from the buffer, the channel's current position is advanced by 1. - /// - /// The returned sample will be a byte value, but in an `i16` with the buffer's original `u8` - /// value centered around 0 (meaning the returned sample will be within the range -128 to 127 - /// instead of 0 to 255). - #[inline] - fn next_sample(&mut self) -> Option { - if let Some(sample) = self.data_at(self.position) { - self.position += 1; - Some(sample as i16 - 128) - } else { - None - } - } + /// Returns the next sample from this channel's buffer. If this channel's buffer is done + /// playing or there is no buffer data at all, `None` is returned. If the next sample was + /// successfully loaded from the buffer, the channel's current position is advanced by 1. + /// + /// The returned sample will be a byte value, but in an `i16` with the buffer's original `u8` + /// value centered around 0 (meaning the returned sample will be within the range -128 to 127 + /// instead of 0 to 255). + #[inline] + fn next_sample(&mut self) -> Option { + if let Some(sample) = self.data_at(self.position) { + self.position += 1; + Some(sample as i16 - 128) + } else { + None + } + } - /// Samples the channel's current audio buffer, advancing the position within that buffer by 1. - /// The channel will automatically stop playing when the end of the buffer is reached and if - /// the channel is not set to loop. `None` is returned if no data can be read from the buffer - /// for any reason, or if the channel is not currently playing. - /// - /// The returned sample will be a byte value, but in an `i16` with the buffer's original `u8` - /// value centered around 0 (meaning the returned sample will be within the range -128 to 127 - /// instead of 0 to 255). - #[inline] - pub fn sample(&mut self) -> Option { - if !self.playing { - return None; - } + /// Samples the channel's current audio buffer, advancing the position within that buffer by 1. + /// The channel will automatically stop playing when the end of the buffer is reached and if + /// the channel is not set to loop. `None` is returned if no data can be read from the buffer + /// for any reason, or if the channel is not currently playing. + /// + /// The returned sample will be a byte value, but in an `i16` with the buffer's original `u8` + /// value centered around 0 (meaning the returned sample will be within the range -128 to 127 + /// instead of 0 to 255). + #[inline] + pub fn sample(&mut self) -> Option { + if !self.playing { + return None; + } - if let Some(sample) = self.next_sample() { - Some((sample as f32 * self.volume) as i16) - } else { - if self.loops { - self.position = 0; - None - } else { - self.stop(); - None - } - } - } + if let Some(sample) = self.next_sample() { + Some((sample as f32 * self.volume) as i16) + } else { + if self.loops { + self.position = 0; + None + } else { + self.stop(); + None + } + } + } - /// Resets the audio channel to a "blank slate", clearing the audio buffer, setting no current - /// audio generator, and turning playback off. - #[inline] - pub fn reset(&mut self) { - self.data.clear(); - self.generator = None; - self.position = 0; - self.playing = false; - } + /// Resets the audio channel to a "blank slate", clearing the audio buffer, setting no current + /// audio generator, and turning playback off. + #[inline] + pub fn reset(&mut self) { + self.data.clear(); + self.generator = None; + self.position = 0; + self.playing = false; + } - /// Copies the data from the given audio buffer into this channel's buffer (clearing it first, - /// and extending the size of the buffer if necessary) and then begins playback from position 0. - /// This also sets the associated [`generator`] to `None`. - #[inline] - pub fn play_buffer(&mut self, buffer: &AudioBuffer, loops: bool) { - self.data.clear(); - self.data.extend(&buffer.data); - self.generator = None; - self.position = 0; - self.playing = true; - self.loops = loops; - } + /// Copies the data from the given audio buffer into this channel's buffer (clearing it first, + /// and extending the size of the buffer if necessary) and then begins playback from position 0. + /// This also sets the associated [`generator`] to `None`. + #[inline] + pub fn play_buffer(&mut self, buffer: &AudioBuffer, loops: bool) { + self.data.clear(); + self.data.extend(&buffer.data); + self.generator = None; + self.position = 0; + self.playing = true; + self.loops = loops; + } - /// Begins playback on this channel from the given [`AudioGenerator`] instance from position 0. - /// This also clears the existing audio buffer contents. - #[inline] - pub fn play_generator(&mut self, generator: Box, loops: bool) { - self.data.clear(); - self.generator = Some(generator); - self.position = 0; - self.playing = true; - self.loops = loops; - } + /// Begins playback on this channel from the given [`AudioGenerator`] instance from position 0. + /// This also clears the existing audio buffer contents. + #[inline] + pub fn play_generator(&mut self, generator: Box, loops: bool) { + self.data.clear(); + self.generator = Some(generator); + self.position = 0; + self.playing = true; + self.loops = loops; + } - /// Returns true if this channel has something that can be played back currently. - #[inline] - pub fn is_playable(&self) -> bool { - !self.data.is_empty() || self.generator.is_some() - } + /// Returns true if this channel has something that can be played back currently. + #[inline] + pub fn is_playable(&self) -> bool { + !self.data.is_empty() || self.generator.is_some() + } - /// Begins playback on this channel, only if playback is currently possible with its current - /// state (if it has some sample data in the buffer or if an [`AudioGenerator`] is set). - /// Resets the position to 0 if playback is started and returns true, otherwise returns false. - #[inline] - pub fn play(&mut self, loops: bool) -> bool { - if self.is_playable() { - self.position = 0; - self.playing = true; - self.loops = loops; - true - } else { - false - } - } + /// Begins playback on this channel, only if playback is currently possible with its current + /// state (if it has some sample data in the buffer or if an [`AudioGenerator`] is set). + /// Resets the position to 0 if playback is started and returns true, otherwise returns false. + #[inline] + pub fn play(&mut self, loops: bool) -> bool { + if self.is_playable() { + self.position = 0; + self.playing = true; + self.loops = loops; + true + } else { + false + } + } - /// Stops playback on this channel. - #[inline] - pub fn stop(&mut self) { - self.playing = false; - } + /// Stops playback on this channel. + #[inline] + pub fn stop(&mut self) { + self.playing = false; + } } ////////////////////////////////////////////////////////////////////////////////////////////////// #[derive(Debug, Error)] pub enum AudioDeviceError { - #[error("That buffer's AudioSpec does not match the device's AudioSpec")] - AudioSpecMismatch, + #[error("That buffer's AudioSpec does not match the device's AudioSpec")] + AudioSpecMismatch, - #[error("The channel index {0} is out of range")] - ChannelIndexOutOfRange(usize), + #[error("The channel index {0} is out of range")] + ChannelIndexOutOfRange(usize), } /// Represents the audio device and performs mixing of all of the [`AudioChannel`]s that are @@ -189,214 +189,214 @@ pub enum AudioDeviceError { /// [`Audio::lock`]. #[derive(Debug)] pub struct AudioDevice { - spec: AudioSpec, - channels: Vec, - pub volume: f32, + spec: AudioSpec, + channels: Vec, + pub volume: f32, } /// SDL audio callback implementation which performs audio mixing, generating the final sample data /// that will be played by the system's audio device. impl AudioCallback for AudioDevice { - type Channel = u8; + type Channel = u8; - fn callback(&mut self, out: &mut [u8]) { - for dest in out.iter_mut() { - let mut sample: i16 = 0; - for channel in self.channels.iter_mut() { - if let Some(this_sample) = channel.sample() { - sample += this_sample; - } - } - sample = ((sample as f32) * self.volume) as i16; - *dest = (sample.clamp(-128, 127) + 128) as u8; - } - } + fn callback(&mut self, out: &mut [u8]) { + for dest in out.iter_mut() { + let mut sample: i16 = 0; + for channel in self.channels.iter_mut() { + if let Some(this_sample) = channel.sample() { + sample += this_sample; + } + } + sample = ((sample as f32) * self.volume) as i16; + *dest = (sample.clamp(-128, 127) + 128) as u8; + } + } } impl AudioDevice { - /// Creates a new [`AudioDevice`] instance, using the given spec as its playback format. - pub fn new(spec: AudioSpec) -> Self { - let mut channels = Vec::new(); - for _ in 0..NUM_CHANNELS { - channels.push(AudioChannel::new()); - } - AudioDevice { - spec, - channels, - volume: 1.0, - } - } + /// Creates a new [`AudioDevice`] instance, using the given spec as its playback format. + pub fn new(spec: AudioSpec) -> Self { + let mut channels = Vec::new(); + for _ in 0..NUM_CHANNELS { + channels.push(AudioChannel::new()); + } + AudioDevice { + spec, + channels, + volume: 1.0, + } + } - /// Returns the spec that this device is currently set to play. All audio to be played via - /// this device must be pre-converted to match this spec! - #[inline] - pub fn spec(&self) -> &AudioSpec { - &self.spec - } + /// Returns the spec that this device is currently set to play. All audio to be played via + /// this device must be pre-converted to match this spec! + #[inline] + pub fn spec(&self) -> &AudioSpec { + &self.spec + } - /// Returns true if any of the audio channels are currently playing, false otherwise. - #[inline] - pub fn is_playing(&self) -> bool { - self.channels.iter().any(|channel| channel.playing) - } + /// Returns true if any of the audio channels are currently playing, false otherwise. + #[inline] + pub fn is_playing(&self) -> bool { + self.channels.iter().any(|channel| channel.playing) + } - /// Stops the specified channel's playback, or does nothing if that channel was not currently - /// playing. This does not affect the channel's other state (data buffer, etc). - pub fn stop_channel(&mut self, channel_index: usize) -> Result<(), AudioDeviceError> { - if channel_index >= NUM_CHANNELS { - Err(AudioDeviceError::ChannelIndexOutOfRange(channel_index)) - } else { - self.channels[channel_index].stop(); - Ok(()) - } - } + /// Stops the specified channel's playback, or does nothing if that channel was not currently + /// playing. This does not affect the channel's other state (data buffer, etc). + pub fn stop_channel(&mut self, channel_index: usize) -> Result<(), AudioDeviceError> { + if channel_index >= NUM_CHANNELS { + Err(AudioDeviceError::ChannelIndexOutOfRange(channel_index)) + } else { + self.channels[channel_index].stop(); + Ok(()) + } + } - /// Stops playback of all channels. - pub fn stop_all(&mut self) { - for channel in self.channels.iter_mut() { - channel.stop(); - } - } + /// Stops playback of all channels. + pub fn stop_all(&mut self) { + for channel in self.channels.iter_mut() { + channel.stop(); + } + } - /// Tries to play the given [`AudioBuffer`] on the first channel found that is not already - /// playing. If a free channel is found, playback will be started by copying the buffer's - /// contents to the channel. The index of the channel is returned. If playback was not started - /// because no channel is free currently, then `None` is returned. - pub fn play_buffer( - &mut self, - buffer: &AudioBuffer, - loops: bool, - ) -> Result, AudioDeviceError> { - if *buffer.spec() != self.spec { - Err(AudioDeviceError::AudioSpecMismatch) - } else { - if let Some((index, channel)) = self.stopped_channels_iter_mut().enumerate().next() { - channel.play_buffer(buffer, loops); - Ok(Some(index)) - } else { - Ok(None) - } - } - } + /// Tries to play the given [`AudioBuffer`] on the first channel found that is not already + /// playing. If a free channel is found, playback will be started by copying the buffer's + /// contents to the channel. The index of the channel is returned. If playback was not started + /// because no channel is free currently, then `None` is returned. + pub fn play_buffer( + &mut self, + buffer: &AudioBuffer, + loops: bool, + ) -> Result, AudioDeviceError> { + if *buffer.spec() != self.spec { + Err(AudioDeviceError::AudioSpecMismatch) + } else { + if let Some((index, channel)) = self.stopped_channels_iter_mut().enumerate().next() { + channel.play_buffer(buffer, loops); + Ok(Some(index)) + } else { + Ok(None) + } + } + } - /// Plays the given [`AudioBuffer`] on the specified channel. Whatever that channel was playing - /// will be interrupted and replaced with a copy of the given buffer's data. - pub fn play_buffer_on_channel( - &mut self, - channel_index: usize, - buffer: &AudioBuffer, - loops: bool, - ) -> Result<(), AudioDeviceError> { - if *buffer.spec() != self.spec { - Err(AudioDeviceError::AudioSpecMismatch) - } else if channel_index >= NUM_CHANNELS { - Err(AudioDeviceError::ChannelIndexOutOfRange(channel_index)) - } else { - self.channels[channel_index].play_buffer(buffer, loops); - Ok(()) - } - } + /// Plays the given [`AudioBuffer`] on the specified channel. Whatever that channel was playing + /// will be interrupted and replaced with a copy of the given buffer's data. + pub fn play_buffer_on_channel( + &mut self, + channel_index: usize, + buffer: &AudioBuffer, + loops: bool, + ) -> Result<(), AudioDeviceError> { + if *buffer.spec() != self.spec { + Err(AudioDeviceError::AudioSpecMismatch) + } else if channel_index >= NUM_CHANNELS { + Err(AudioDeviceError::ChannelIndexOutOfRange(channel_index)) + } else { + self.channels[channel_index].play_buffer(buffer, loops); + Ok(()) + } + } - /// Tries to play the given [`AudioGenerator`] on the first channel found that is not already - /// playing. If a free channel is found, playback will be started and the index of the channel - /// will be returned. If playback was not started because no channel is free currently, then - /// `None` is returned. - pub fn play_generator( - &mut self, - generator: Box, - loops: bool, - ) -> Result, AudioDeviceError> { - if let Some((index, channel)) = self.stopped_channels_iter_mut().enumerate().next() { - channel.play_generator(generator, loops); - Ok(Some(index)) - } else { - Ok(None) - } - } + /// Tries to play the given [`AudioGenerator`] on the first channel found that is not already + /// playing. If a free channel is found, playback will be started and the index of the channel + /// will be returned. If playback was not started because no channel is free currently, then + /// `None` is returned. + pub fn play_generator( + &mut self, + generator: Box, + loops: bool, + ) -> Result, AudioDeviceError> { + if let Some((index, channel)) = self.stopped_channels_iter_mut().enumerate().next() { + channel.play_generator(generator, loops); + Ok(Some(index)) + } else { + Ok(None) + } + } - /// Plays the given [`AudioGenerator`] on the specified channel. Whatever that channel was - /// playing will be interrupted and replaced. - pub fn play_generator_on_channel( - &mut self, - channel_index: usize, - generator: Box, - loops: bool, - ) -> Result<(), AudioDeviceError> { - if channel_index >= NUM_CHANNELS { - Err(AudioDeviceError::ChannelIndexOutOfRange(channel_index)) - } else { - self.channels[channel_index].play_generator(generator, loops); - Ok(()) - } - } + /// Plays the given [`AudioGenerator`] on the specified channel. Whatever that channel was + /// playing will be interrupted and replaced. + pub fn play_generator_on_channel( + &mut self, + channel_index: usize, + generator: Box, + loops: bool, + ) -> Result<(), AudioDeviceError> { + if channel_index >= NUM_CHANNELS { + Err(AudioDeviceError::ChannelIndexOutOfRange(channel_index)) + } else { + self.channels[channel_index].play_generator(generator, loops); + Ok(()) + } + } - /// Returns an iterator of any [`AudioChannel`]s that are currently playing. - #[inline] - pub fn playing_channels_iter(&mut self) -> impl Iterator { - self.channels.iter().filter(|channel| channel.playing) - } + /// Returns an iterator of any [`AudioChannel`]s that are currently playing. + #[inline] + pub fn playing_channels_iter(&mut self) -> impl Iterator { + self.channels.iter().filter(|channel| channel.playing) + } - /// Returns an iterator of mutable [`AudioChannel`]s that are currently playing. - #[inline] - pub fn playing_channels_iter_mut(&mut self) -> impl Iterator { - self.channels.iter_mut().filter(|channel| channel.playing) - } + /// Returns an iterator of mutable [`AudioChannel`]s that are currently playing. + #[inline] + pub fn playing_channels_iter_mut(&mut self) -> impl Iterator { + self.channels.iter_mut().filter(|channel| channel.playing) + } - /// Returns an iterator of [`AudioChannel`]s that are not currently playing. - #[inline] - pub fn stopped_channels_iter(&mut self) -> impl Iterator { - self.channels.iter().filter(|channel| !channel.playing) - } + /// Returns an iterator of [`AudioChannel`]s that are not currently playing. + #[inline] + pub fn stopped_channels_iter(&mut self) -> impl Iterator { + self.channels.iter().filter(|channel| !channel.playing) + } - /// Returns an iterator of mutable [`AudioChannel`]s that are not currently playing. - #[inline] - pub fn stopped_channels_iter_mut(&mut self) -> impl Iterator { - self.channels.iter_mut().filter(|channel| !channel.playing) - } + /// Returns an iterator of mutable [`AudioChannel`]s that are not currently playing. + #[inline] + pub fn stopped_channels_iter_mut(&mut self) -> impl Iterator { + self.channels.iter_mut().filter(|channel| !channel.playing) + } - /// Returns an iterator of all [`AudioChannel`]s. - #[inline] - pub fn channels_iter(&mut self) -> impl Iterator { - self.channels.iter() - } + /// Returns an iterator of all [`AudioChannel`]s. + #[inline] + pub fn channels_iter(&mut self) -> impl Iterator { + self.channels.iter() + } - /// Returns an iterator of all [`AudioChannel`]s as mutable references. - #[inline] - pub fn channels_iter_mut(&mut self) -> impl Iterator { - self.channels.iter_mut() - } + /// Returns an iterator of all [`AudioChannel`]s as mutable references. + #[inline] + pub fn channels_iter_mut(&mut self) -> impl Iterator { + self.channels.iter_mut() + } - /// Returns a reference to the specified [`AudioChannel`] or `None` if the index specified - /// is not valid. - #[inline] - pub fn get(&self, index: usize) -> Option<&AudioChannel> { - self.channels.get(index) - } + /// Returns a reference to the specified [`AudioChannel`] or `None` if the index specified + /// is not valid. + #[inline] + pub fn get(&self, index: usize) -> Option<&AudioChannel> { + self.channels.get(index) + } - /// Returns a mutable reference to the specified [`AudioChannel`] or `None` if the index - /// specified is not valid. - #[inline] - pub fn get_mut(&mut self, index: usize) -> Option<&mut AudioChannel> { - self.channels.get_mut(index) - } + /// Returns a mutable reference to the specified [`AudioChannel`] or `None` if the index + /// specified is not valid. + #[inline] + pub fn get_mut(&mut self, index: usize) -> Option<&mut AudioChannel> { + self.channels.get_mut(index) + } } impl Index for AudioDevice { - type Output = AudioChannel; + type Output = AudioChannel; - /// Returns a reference to the specified [`AudioChannel`] or panics if the index specified is - /// not valid. - #[inline] - fn index(&self, index: usize) -> &Self::Output { - self.get(index).unwrap() - } + /// Returns a reference to the specified [`AudioChannel`] or panics if the index specified is + /// not valid. + #[inline] + fn index(&self, index: usize) -> &Self::Output { + self.get(index).unwrap() + } } impl IndexMut for AudioDevice { - /// Returns a mutable reference to the specified [`AudioChannel`] or panics if the index - /// specified is not valid. - #[inline] - fn index_mut(&mut self, index: usize) -> &mut Self::Output { - self.get_mut(index).unwrap() - } + /// Returns a mutable reference to the specified [`AudioChannel`] or panics if the index + /// specified is not valid. + #[inline] + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + self.get_mut(index).unwrap() + } } diff --git a/libretrogd/src/audio/mod.rs b/libretrogd/src/audio/mod.rs index f47e3d3..3db906a 100644 --- a/libretrogd/src/audio/mod.rs +++ b/libretrogd/src/audio/mod.rs @@ -32,43 +32,43 @@ pub const TARGET_AUDIO_CHANNELS: u8 = 1; /// to know what format an audio buffer is in and to specify conversion formats, etc. #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub struct AudioSpec { - frequency: u32, - channels: u8, - format: AudioFormat, + frequency: u32, + channels: u8, + format: AudioFormat, } impl AudioSpec { - /// Creates a new `AudioSpec` with the properties specified. - /// - /// # Arguments - /// - /// * `frequency`: the frequency of the audio - /// * `channels`: the number of channels of the audio (e.g. 1 = mono, 2 = stereo, etc) - /// * `format`: indicates the format of the bytes making up the audio buffer. - pub fn new(frequency: u32, channels: u8, format: AudioFormat) -> Self { - AudioSpec { - frequency, - channels, - format, - } - } + /// Creates a new `AudioSpec` with the properties specified. + /// + /// # Arguments + /// + /// * `frequency`: the frequency of the audio + /// * `channels`: the number of channels of the audio (e.g. 1 = mono, 2 = stereo, etc) + /// * `format`: indicates the format of the bytes making up the audio buffer. + pub fn new(frequency: u32, channels: u8, format: AudioFormat) -> Self { + AudioSpec { + frequency, + channels, + format, + } + } - #[inline] - pub fn frequency(&self) -> u32 { - self.frequency - } + #[inline] + pub fn frequency(&self) -> u32 { + self.frequency + } - #[inline] - pub fn channels(&self) -> u8 { - self.channels - } + #[inline] + pub fn channels(&self) -> u8 { + self.channels + } - /// An SDL2 [`sdl2::audio::AudioFormat`] value indicating the audio format of the bytes making - /// up an audio buffer. - #[inline] - pub fn format(&self) -> AudioFormat { - self.format - } + /// An SDL2 [`sdl2::audio::AudioFormat`] value indicating the audio format of the bytes making + /// up an audio buffer. + #[inline] + pub fn format(&self) -> AudioFormat { + self.format + } } ////////////////////////////////////////////////////////////////////////////////////////////////// @@ -77,104 +77,104 @@ impl AudioSpec { /// Used to implement custom/dynamic audio generation. pub trait AudioGenerator: Send { - /// Generates and returns the sample for the given playback position. `None` is returned if - /// there is no sample for that position (e.g. it might be past the "end"). - fn gen_sample(&mut self, position: usize) -> Option; + /// Generates and returns the sample for the given playback position. `None` is returned if + /// there is no sample for that position (e.g. it might be past the "end"). + fn gen_sample(&mut self, position: usize) -> Option; } ////////////////////////////////////////////////////////////////////////////////////////////////// #[derive(Debug, Error)] pub enum AudioError { - #[error("Failed to open audio device for playback: {0}")] - OpenDeviceFailed(String), + #[error("Failed to open audio device for playback: {0}")] + OpenDeviceFailed(String), } /// Top-level abstraction over the system's audio output device. To play audio or change other /// playback properties, you will need to lock the audio device via [`Audio::lock`] to obtain an /// [`AudioDevice`]. pub struct Audio { - spec: AudioSpec, - sdl_audio_device: sdl2::audio::AudioDevice, + spec: AudioSpec, + sdl_audio_device: sdl2::audio::AudioDevice, } impl std::fmt::Debug for Audio { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("Audio") - .field("spec", &self.spec) - .finish_non_exhaustive() - } + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Audio") + .field("spec", &self.spec) + .finish_non_exhaustive() + } } impl Audio { - /// Creates a new [`Audio`] instance, wrapping the given SDL [`sdl2::audio::AudioSubsystem`]. - /// The `desired_spec` given specifies the target audio playback format. - /// - /// Ideally, you should not be creating an instance of this yourself and should just use the - /// one provided by [`crate::system::System`]. - pub fn new( - desired_spec: AudioSpecDesired, - sdl_audio_subsystem: &AudioSubsystem, - ) -> Result { - let mut spec = None; - let sdl_audio_device = - match sdl_audio_subsystem.open_playback(None, &desired_spec, |opened_spec| { - let our_spec = AudioSpec::new( - opened_spec.freq as u32, - opened_spec.channels, - opened_spec.format, - ); - spec = Some(our_spec); - AudioDevice::new(our_spec) - }) { - Ok(audio_device) => audio_device, - Err(error) => return Err(AudioError::OpenDeviceFailed(error)), - }; + /// Creates a new [`Audio`] instance, wrapping the given SDL [`sdl2::audio::AudioSubsystem`]. + /// The `desired_spec` given specifies the target audio playback format. + /// + /// Ideally, you should not be creating an instance of this yourself and should just use the + /// one provided by [`crate::system::System`]. + pub fn new( + desired_spec: AudioSpecDesired, + sdl_audio_subsystem: &AudioSubsystem, + ) -> Result { + let mut spec = None; + let sdl_audio_device = + match sdl_audio_subsystem.open_playback(None, &desired_spec, |opened_spec| { + let our_spec = AudioSpec::new( + opened_spec.freq as u32, + opened_spec.channels, + opened_spec.format, + ); + spec = Some(our_spec); + AudioDevice::new(our_spec) + }) { + Ok(audio_device) => audio_device, + Err(error) => return Err(AudioError::OpenDeviceFailed(error)), + }; - if let Some(spec) = spec { - Ok(Audio { - spec, - sdl_audio_device, - }) - } else { - Err(AudioError::OpenDeviceFailed(String::from( - "Device initialization failed to set AudioSpec", - ))) - } - } + if let Some(spec) = spec { + Ok(Audio { + spec, + sdl_audio_device, + }) + } else { + Err(AudioError::OpenDeviceFailed(String::from( + "Device initialization failed to set AudioSpec", + ))) + } + } - /// Returns current audio device's audio specification/format for playback. All [`AudioBuffer`]s - /// that are to be used for playback must be converted to match this before they can be played. - #[inline] - pub fn spec(&self) -> &AudioSpec { - &self.spec - } + /// Returns current audio device's audio specification/format for playback. All [`AudioBuffer`]s + /// that are to be used for playback must be converted to match this before they can be played. + #[inline] + pub fn spec(&self) -> &AudioSpec { + &self.spec + } - /// Returns the current status of the audio device (e.g. whether it is paused, stopped, etc). - #[inline] - pub fn status(&self) -> sdl2::audio::AudioStatus { - self.sdl_audio_device.status() - } + /// Returns the current status of the audio device (e.g. whether it is paused, stopped, etc). + #[inline] + pub fn status(&self) -> sdl2::audio::AudioStatus { + self.sdl_audio_device.status() + } - /// Pauses all audio playback. - #[inline] - pub fn pause(&mut self) { - self.sdl_audio_device.pause() - } + /// Pauses all audio playback. + #[inline] + pub fn pause(&mut self) { + self.sdl_audio_device.pause() + } - /// Resumes all audio playback. - #[inline] - pub fn resume(&mut self) { - self.sdl_audio_device.resume() - } + /// Resumes all audio playback. + #[inline] + pub fn resume(&mut self) { + self.sdl_audio_device.resume() + } - /// Locks the audio device so that new audio data can be provided or playback altered. A - /// [`AudioDevice`] instance is returned on successful lock which can be used to interact with - /// the actual system's audio playback. The audio device is unlocked once this instance is - /// dropped. Ideally, you will want to keep the audio device for **as _short_ a time as - /// possible!** - #[inline] - pub fn lock(&mut self) -> sdl2::audio::AudioDeviceLockGuard { - self.sdl_audio_device.lock() - } + /// Locks the audio device so that new audio data can be provided or playback altered. A + /// [`AudioDevice`] instance is returned on successful lock which can be used to interact with + /// the actual system's audio playback. The audio device is unlocked once this instance is + /// dropped. Ideally, you will want to keep the audio device for **as _short_ a time as + /// possible!** + #[inline] + pub fn lock(&mut self) -> sdl2::audio::AudioDeviceLockGuard { + self.sdl_audio_device.lock() + } } diff --git a/libretrogd/src/audio/queue.rs b/libretrogd/src/audio/queue.rs index 11a409e..ef1ecbc 100644 --- a/libretrogd/src/audio/queue.rs +++ b/libretrogd/src/audio/queue.rs @@ -4,82 +4,82 @@ use std::rc::Rc; use crate::audio::*; pub enum AudioCommand { - StopChannel(usize), - StopAllChannels, - PlayBuffer { - buffer: AudioBuffer, - loops: bool, - }, - PlayRcBuffer { - buffer: Rc, - loops: bool, - }, - PlayBufferOnChannel { - channel: usize, - buffer: AudioBuffer, - loops: bool, - }, - PlayRcBufferOnChannel { - channel: usize, - buffer: Rc, - loops: bool, - }, - PlayGenerator { - generator: Box, - loops: bool, - }, - PlayGeneratorOnChannel { - channel: usize, - generator: Box, - loops: bool, - }, + StopChannel(usize), + StopAllChannels, + PlayBuffer { + buffer: AudioBuffer, + loops: bool, + }, + PlayRcBuffer { + buffer: Rc, + loops: bool, + }, + PlayBufferOnChannel { + channel: usize, + buffer: AudioBuffer, + loops: bool, + }, + PlayRcBufferOnChannel { + channel: usize, + buffer: Rc, + loops: bool, + }, + PlayGenerator { + generator: Box, + loops: bool, + }, + PlayGeneratorOnChannel { + channel: usize, + generator: Box, + loops: bool, + }, } impl std::fmt::Debug for AudioCommand { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - use AudioCommand::*; - match self { - StopChannel(n) => write!(f, "StopChannel({})", n), - StopAllChannels => write!(f, "StopAllChannels"), - PlayBuffer { buffer, loops } => { - f.debug_struct("PlayBuffer") - .field("buffer", buffer) - .field("loops", loops) - .finish() - }, - PlayRcBuffer { buffer, loops } => { - f.debug_struct("PlayRcBuffer") - .field("buffer", buffer) - .field("loops", loops) - .finish() - }, - PlayBufferOnChannel { channel, buffer, loops } => { - f.debug_struct("PlayBufferOnChannel") - .field("channel", channel) - .field("buffer", buffer) - .field("loops", loops) - .finish() - }, - PlayRcBufferOnChannel { channel, buffer, loops } => { - f.debug_struct("PlayRcBufferOnChannel") - .field("channel", channel) - .field("buffer", buffer) - .field("loops", loops) - .finish() - }, - PlayGenerator { loops, .. } => { - f.debug_struct("PlayGenerator") - .field("loops", loops) - .finish_non_exhaustive() - }, - PlayGeneratorOnChannel { channel, loops, .. } => { - f.debug_struct("PlayGeneratorOnChannel") - .field("channel", channel) - .field("loops", loops) - .finish_non_exhaustive() - }, - } - } + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + use AudioCommand::*; + match self { + StopChannel(n) => write!(f, "StopChannel({})", n), + StopAllChannels => write!(f, "StopAllChannels"), + PlayBuffer { buffer, loops } => { + f.debug_struct("PlayBuffer") + .field("buffer", buffer) + .field("loops", loops) + .finish() + } + PlayRcBuffer { buffer, loops } => { + f.debug_struct("PlayRcBuffer") + .field("buffer", buffer) + .field("loops", loops) + .finish() + } + PlayBufferOnChannel { channel, buffer, loops } => { + f.debug_struct("PlayBufferOnChannel") + .field("channel", channel) + .field("buffer", buffer) + .field("loops", loops) + .finish() + } + PlayRcBufferOnChannel { channel, buffer, loops } => { + f.debug_struct("PlayRcBufferOnChannel") + .field("channel", channel) + .field("buffer", buffer) + .field("loops", loops) + .finish() + } + PlayGenerator { loops, .. } => { + f.debug_struct("PlayGenerator") + .field("loops", loops) + .finish_non_exhaustive() + } + PlayGeneratorOnChannel { channel, loops, .. } => { + f.debug_struct("PlayGeneratorOnChannel") + .field("channel", channel) + .field("loops", loops) + .finish_non_exhaustive() + } + } + } } /// A convenience abstraction that can be used to queue up commands to be issued to an @@ -89,199 +89,199 @@ impl std::fmt::Debug for AudioCommand { /// manner. #[derive(Debug)] pub struct AudioQueue { - spec: AudioSpec, - commands: VecDeque, + spec: AudioSpec, + commands: VecDeque, } impl AudioQueue { - /// Creates and returns a new [`AudioQueue`] instance. - pub fn new(audio: &Audio) -> Self { - AudioQueue { - spec: audio.spec, - commands: VecDeque::new(), - } - } + /// Creates and returns a new [`AudioQueue`] instance. + pub fn new(audio: &Audio) -> Self { + AudioQueue { + spec: audio.spec, + commands: VecDeque::new(), + } + } - /// Returns the spec that this queue is currently set to play. All audio to be played via - /// this queue must be pre-converted to match this spec! This spec is a copy of the one that - /// was obtained from the [`Audio`] instance used to create this [`AudioQueue`]. - #[inline] - pub fn spec(&self) -> &AudioSpec { - &self.spec - } + /// Returns the spec that this queue is currently set to play. All audio to be played via + /// this queue must be pre-converted to match this spec! This spec is a copy of the one that + /// was obtained from the [`Audio`] instance used to create this [`AudioQueue`]. + #[inline] + pub fn spec(&self) -> &AudioSpec { + &self.spec + } - /// Queues a stop command for the given channel. - pub fn stop_channel(&mut self, channel_index: usize) -> Result<(), AudioDeviceError> { - if channel_index >= NUM_CHANNELS { - Err(AudioDeviceError::ChannelIndexOutOfRange(channel_index)) - } else { - self.commands.push_back(AudioCommand::StopChannel(channel_index)); - Ok(()) - } - } + /// Queues a stop command for the given channel. + pub fn stop_channel(&mut self, channel_index: usize) -> Result<(), AudioDeviceError> { + if channel_index >= NUM_CHANNELS { + Err(AudioDeviceError::ChannelIndexOutOfRange(channel_index)) + } else { + self.commands.push_back(AudioCommand::StopChannel(channel_index)); + Ok(()) + } + } - /// Queues a command that will stop playback on all channels. - pub fn stop_all(&mut self) { - self.commands.push_back(AudioCommand::StopAllChannels); - } + /// Queues a command that will stop playback on all channels. + pub fn stop_all(&mut self) { + self.commands.push_back(AudioCommand::StopAllChannels); + } - /// Queues a command to play a copy of the given [`AudioBuffer`]'s data. The buffer will be - /// played on the first channel found that is not already playing. If all channels are already - /// playing, then nothing will be done. - pub fn play_buffer( - &mut self, - buffer: &AudioBuffer, - loops: bool, - ) -> Result<(), AudioDeviceError> { - if *buffer.spec() != self.spec { - Err(AudioDeviceError::AudioSpecMismatch) - } else { - self.commands.push_back(AudioCommand::PlayBuffer { - buffer: buffer.clone(), - loops, - }); - Ok(()) - } - } + /// Queues a command to play a copy of the given [`AudioBuffer`]'s data. The buffer will be + /// played on the first channel found that is not already playing. If all channels are already + /// playing, then nothing will be done. + pub fn play_buffer( + &mut self, + buffer: &AudioBuffer, + loops: bool, + ) -> Result<(), AudioDeviceError> { + if *buffer.spec() != self.spec { + Err(AudioDeviceError::AudioSpecMismatch) + } else { + self.commands.push_back(AudioCommand::PlayBuffer { + buffer: buffer.clone(), + loops, + }); + Ok(()) + } + } - /// Queues a command to play the given [`AudioBuffer`]'s data. The buffer will be played on - /// the first channel found that is not already playing. If all channels are already playing, - /// then nothing will be done. This method is more performant than [`AudioQueue::play_buffer`], - /// as that method will always immediately copy the given buffer to create the queued command. - pub fn play_buffer_rc( - &mut self, - buffer: Rc, - loops: bool, - ) -> Result<(), AudioDeviceError> { - if *buffer.spec() != self.spec { - Err(AudioDeviceError::AudioSpecMismatch) - } else { - self.commands.push_back(AudioCommand::PlayRcBuffer { - buffer, - loops, - }); - Ok(()) - } - } + /// Queues a command to play the given [`AudioBuffer`]'s data. The buffer will be played on + /// the first channel found that is not already playing. If all channels are already playing, + /// then nothing will be done. This method is more performant than [`AudioQueue::play_buffer`], + /// as that method will always immediately copy the given buffer to create the queued command. + pub fn play_buffer_rc( + &mut self, + buffer: Rc, + loops: bool, + ) -> Result<(), AudioDeviceError> { + if *buffer.spec() != self.spec { + Err(AudioDeviceError::AudioSpecMismatch) + } else { + self.commands.push_back(AudioCommand::PlayRcBuffer { + buffer, + loops, + }); + Ok(()) + } + } - /// Queues a command to play a copy of the given [`AudioBuffer`]'s data on the channel - /// specified. Whatever that channel was playing will be interrupted to begin playing this - /// buffer. - pub fn play_buffer_on_channel( - &mut self, - channel_index: usize, - buffer: &AudioBuffer, - loops: bool, - ) -> Result<(), AudioDeviceError> { - if *buffer.spec() != self.spec { - Err(AudioDeviceError::AudioSpecMismatch) - } else if channel_index >= NUM_CHANNELS { - Err(AudioDeviceError::ChannelIndexOutOfRange(channel_index)) - } else { - self.commands.push_back(AudioCommand::PlayBufferOnChannel { - channel: channel_index, - buffer: buffer.clone(), - loops, - }); - Ok(()) - } - } + /// Queues a command to play a copy of the given [`AudioBuffer`]'s data on the channel + /// specified. Whatever that channel was playing will be interrupted to begin playing this + /// buffer. + pub fn play_buffer_on_channel( + &mut self, + channel_index: usize, + buffer: &AudioBuffer, + loops: bool, + ) -> Result<(), AudioDeviceError> { + if *buffer.spec() != self.spec { + Err(AudioDeviceError::AudioSpecMismatch) + } else if channel_index >= NUM_CHANNELS { + Err(AudioDeviceError::ChannelIndexOutOfRange(channel_index)) + } else { + self.commands.push_back(AudioCommand::PlayBufferOnChannel { + channel: channel_index, + buffer: buffer.clone(), + loops, + }); + Ok(()) + } + } - /// Queues a command to play the given [`AudioBuffer`]'s data on the channel specified. Whatever - /// that channel was playing will be interrupted to begin playing this buffer. This method is - /// more performant than [`AudioQueue::play_buffer_on_channel`], as that method will always - /// immediately copy the given buffer to create the queued command. - pub fn play_buffer_rc_on_channel( - &mut self, - channel_index: usize, - buffer: Rc, - loops: bool, - ) -> Result<(), AudioDeviceError> { - if *buffer.spec() != self.spec { - Err(AudioDeviceError::AudioSpecMismatch) - } else if channel_index >= NUM_CHANNELS { - Err(AudioDeviceError::ChannelIndexOutOfRange(channel_index)) - } else { - self.commands.push_back(AudioCommand::PlayRcBufferOnChannel { - channel: channel_index, - buffer, - loops, - }); - Ok(()) - } - } + /// Queues a command to play the given [`AudioBuffer`]'s data on the channel specified. Whatever + /// that channel was playing will be interrupted to begin playing this buffer. This method is + /// more performant than [`AudioQueue::play_buffer_on_channel`], as that method will always + /// immediately copy the given buffer to create the queued command. + pub fn play_buffer_rc_on_channel( + &mut self, + channel_index: usize, + buffer: Rc, + loops: bool, + ) -> Result<(), AudioDeviceError> { + if *buffer.spec() != self.spec { + Err(AudioDeviceError::AudioSpecMismatch) + } else if channel_index >= NUM_CHANNELS { + Err(AudioDeviceError::ChannelIndexOutOfRange(channel_index)) + } else { + self.commands.push_back(AudioCommand::PlayRcBufferOnChannel { + channel: channel_index, + buffer, + loops, + }); + Ok(()) + } + } - /// Queues a command to play the given [`AudioGenerator`] on the first channel found that is - /// not already playing. If all channels are already playing, then nothing will be done. - pub fn play_generator( - &mut self, - generator: Box, - loops: bool, - ) -> Result<(), AudioDeviceError> { - self.commands.push_back(AudioCommand::PlayGenerator { generator, loops }); - Ok(()) - } + /// Queues a command to play the given [`AudioGenerator`] on the first channel found that is + /// not already playing. If all channels are already playing, then nothing will be done. + pub fn play_generator( + &mut self, + generator: Box, + loops: bool, + ) -> Result<(), AudioDeviceError> { + self.commands.push_back(AudioCommand::PlayGenerator { generator, loops }); + Ok(()) + } - /// Queues a command to play the given [`AudioGenerator`] on the channel specified. Whatever - /// that channel was playing will be interrupted to begin playing this generator. - pub fn play_generator_on_channel( - &mut self, - channel_index: usize, - generator: Box, - loops: bool, - ) -> Result<(), AudioDeviceError> { - self.commands.push_back(AudioCommand::PlayGeneratorOnChannel { - channel: channel_index, - generator, - loops, - }); - Ok(()) - } + /// Queues a command to play the given [`AudioGenerator`] on the channel specified. Whatever + /// that channel was playing will be interrupted to begin playing this generator. + pub fn play_generator_on_channel( + &mut self, + channel_index: usize, + generator: Box, + loops: bool, + ) -> Result<(), AudioDeviceError> { + self.commands.push_back(AudioCommand::PlayGeneratorOnChannel { + channel: channel_index, + generator, + loops, + }); + Ok(()) + } - /// Flushes the queued commands, issuing them in the same order they were created, to the - /// given [`AudioDevice`]. - pub fn apply_to_device(&mut self, device: &mut AudioDevice) -> Result<(), AudioDeviceError> { - loop { - if let Some(command) = self.commands.pop_front() { - use AudioCommand::*; - match command { - StopChannel(channel_index) => { - device.stop_channel(channel_index)?; - }, - StopAllChannels => { - device.stop_all(); - }, - PlayBuffer { buffer, loops } => { - device.play_buffer(&buffer, loops)?; - } - PlayRcBuffer { buffer, loops } => { - device.play_buffer(&buffer, loops)?; - }, - PlayBufferOnChannel { channel, buffer, loops } => { - device.play_buffer_on_channel(channel, &buffer, loops)?; - } - PlayRcBufferOnChannel { channel, buffer, loops } => { - device.play_buffer_on_channel(channel, &buffer, loops)?; - }, - PlayGenerator { generator, loops } => { - device.play_generator(generator, loops)?; - }, - PlayGeneratorOnChannel { channel, generator, loops } => { - device.play_generator_on_channel(channel, generator, loops)?; - }, - } - } else { - return Ok(()) - } - } - } + /// Flushes the queued commands, issuing them in the same order they were created, to the + /// given [`AudioDevice`]. + pub fn apply_to_device(&mut self, device: &mut AudioDevice) -> Result<(), AudioDeviceError> { + loop { + if let Some(command) = self.commands.pop_front() { + use AudioCommand::*; + match command { + StopChannel(channel_index) => { + device.stop_channel(channel_index)?; + } + StopAllChannels => { + device.stop_all(); + } + PlayBuffer { buffer, loops } => { + device.play_buffer(&buffer, loops)?; + } + PlayRcBuffer { buffer, loops } => { + device.play_buffer(&buffer, loops)?; + } + PlayBufferOnChannel { channel, buffer, loops } => { + device.play_buffer_on_channel(channel, &buffer, loops)?; + } + PlayRcBufferOnChannel { channel, buffer, loops } => { + device.play_buffer_on_channel(channel, &buffer, loops)?; + } + PlayGenerator { generator, loops } => { + device.play_generator(generator, loops)?; + } + PlayGeneratorOnChannel { channel, generator, loops } => { + device.play_generator_on_channel(channel, generator, loops)?; + } + } + } else { + return Ok(()); + } + } + } - /// Flushes the queued commands, issuing them in the same order they were created, to the - /// given [`Audio`] instance. This method automatically handles obtaining a locked - /// [`AudioDevice`], and so is a bit more convenient to use if you don't actually need to - /// interact with the [`AudioDevice`] itself in your code. - pub fn apply(&mut self, audio: &mut Audio) -> Result<(), AudioDeviceError> { - let mut device = audio.lock(); - self.apply_to_device(&mut device) - } + /// Flushes the queued commands, issuing them in the same order they were created, to the + /// given [`Audio`] instance. This method automatically handles obtaining a locked + /// [`AudioDevice`], and so is a bit more convenient to use if you don't actually need to + /// interact with the [`AudioDevice`] itself in your code. + pub fn apply(&mut self, audio: &mut Audio) -> Result<(), AudioDeviceError> { + let mut device = audio.lock(); + self.apply_to_device(&mut device) + } } \ No newline at end of file diff --git a/libretrogd/src/base/mod.rs b/libretrogd/src/base/mod.rs index 95e208e..4361893 100644 --- a/libretrogd/src/base/mod.rs +++ b/libretrogd/src/base/mod.rs @@ -186,9 +186,9 @@ pub fn main_loop( mut app: ContextType, initial_state: State, ) -> Result<(), MainLoopError> -where - ContextType: AppContext, - State: AppState + 'static, + where + ContextType: AppContext, + State: AppState + 'static, { let mut states = States::new(); states.push(initial_state)?; diff --git a/libretrogd/src/entities/mod.rs b/libretrogd/src/entities/mod.rs index 51b76c6..6926eef 100644 --- a/libretrogd/src/entities/mod.rs +++ b/libretrogd/src/entities/mod.rs @@ -11,6 +11,7 @@ pub type EntityId = usize; // alias `Component` to always be `'static` ... pub trait Component: 'static {} + impl Component for T {} pub type ComponentStore = RefCell>; @@ -18,231 +19,231 @@ pub type RefComponents<'a, T> = Ref<'a, HashMap>; pub type RefMutComponents<'a, T> = RefMut<'a, HashMap>; pub trait GenericComponentStore: AsAny { - /// Returns true if this component store currently has a component for the specified entity. - fn has(&self, entity: EntityId) -> bool; + /// Returns true if this component store currently has a component for the specified entity. + fn has(&self, entity: EntityId) -> bool; - /// If this component store has a component for the specified entity, removes it and returns - /// true. Otherwise, returns false. - fn remove(&mut self, entity: EntityId) -> bool; + /// If this component store has a component for the specified entity, removes it and returns + /// true. Otherwise, returns false. + fn remove(&mut self, entity: EntityId) -> bool; - /// Removes all components from this store. - fn clear(&mut self); + /// Removes all components from this store. + fn clear(&mut self); } impl GenericComponentStore for ComponentStore { - #[inline] - fn has(&self, entity: EntityId) -> bool { - self.borrow().contains_key(&entity) - } + #[inline] + fn has(&self, entity: EntityId) -> bool { + self.borrow().contains_key(&entity) + } - #[inline] - fn remove(&mut self, entity: EntityId) -> bool { - self.get_mut().remove(&entity).is_some() - } + #[inline] + fn remove(&mut self, entity: EntityId) -> bool { + self.get_mut().remove(&entity).is_some() + } - #[inline] - fn clear(&mut self) { - self.get_mut().clear(); - } + #[inline] + fn clear(&mut self) { + self.get_mut().clear(); + } } #[inline] pub fn as_component_store( - collection: &dyn GenericComponentStore, + collection: &dyn GenericComponentStore, ) -> &ComponentStore { - collection.as_any().downcast_ref().unwrap() + collection.as_any().downcast_ref().unwrap() } #[inline] pub fn as_component_store_mut( - collection: &mut dyn GenericComponentStore, + collection: &mut dyn GenericComponentStore, ) -> &mut ComponentStore { - collection.as_any_mut().downcast_mut().unwrap() + collection.as_any_mut().downcast_mut().unwrap() } /////////////////////////////////////////////////////////////////////////////////////////////////// /// Entity manager. Stores entity components and manages entity IDs. pub struct Entities { - entities: HashSet, - component_stores: HashMap>, - next_id: EntityId, + entities: HashSet, + component_stores: HashMap>, + next_id: EntityId, } impl std::fmt::Debug for Entities { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("Entities") - .field("entities.len()", &self.entities.len()) - .field("component_stores.keys()", &self.component_stores.keys()) - .field("next_id", &self.next_id) - .finish_non_exhaustive() - } + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Entities") + .field("entities.len()", &self.entities.len()) + .field("component_stores.keys()", &self.component_stores.keys()) + .field("next_id", &self.next_id) + .finish_non_exhaustive() + } } impl Entities { - /// Creates and returns a new instance of an entity manager. - pub fn new() -> Self { - Entities { - entities: HashSet::new(), - component_stores: HashMap::new(), - next_id: 0, - } - } + /// Creates and returns a new instance of an entity manager. + pub fn new() -> Self { + Entities { + entities: HashSet::new(), + component_stores: HashMap::new(), + next_id: 0, + } + } - #[inline] - fn has_component_store(&self) -> bool { - let type_id = TypeId::of::(); - self.component_stores.contains_key(&type_id) - } + #[inline] + fn has_component_store(&self) -> bool { + let type_id = TypeId::of::(); + self.component_stores.contains_key(&type_id) + } - fn get_component_store(&self) -> Option<&ComponentStore> { - if !self.has_component_store::() { - None - } else { - let type_id = TypeId::of::(); - Some(as_component_store( - self.component_stores.get(&type_id).unwrap().as_ref(), - )) - } - } + fn get_component_store(&self) -> Option<&ComponentStore> { + if !self.has_component_store::() { + None + } else { + let type_id = TypeId::of::(); + Some(as_component_store( + self.component_stores.get(&type_id).unwrap().as_ref(), + )) + } + } - fn add_component_store(&mut self) -> &ComponentStore { - if self.has_component_store::() { - self.get_component_store().unwrap() - } else { - let component_store = ComponentStore::::new(HashMap::new()); - let type_id = TypeId::of::(); - self.component_stores - .insert(type_id, Box::new(component_store)); - as_component_store(self.component_stores.get(&type_id).unwrap().as_ref()) - } - } + fn add_component_store(&mut self) -> &ComponentStore { + if self.has_component_store::() { + self.get_component_store().unwrap() + } else { + let component_store = ComponentStore::::new(HashMap::new()); + let type_id = TypeId::of::(); + self.component_stores + .insert(type_id, Box::new(component_store)); + as_component_store(self.component_stores.get(&type_id).unwrap().as_ref()) + } + } - /// Returns true if the entity manager currently is aware of an entity with the given ID. - #[inline] - pub fn has_entity(&self, entity: EntityId) -> bool { - self.entities.contains(&entity) - } + /// Returns true if the entity manager currently is aware of an entity with the given ID. + #[inline] + pub fn has_entity(&self, entity: EntityId) -> bool { + self.entities.contains(&entity) + } - /// Returns a previously unused entity ID. Use this to "create" a new entity. - pub fn new_entity(&mut self) -> EntityId { - let new_entity_id = self.next_id; - self.next_id = self.next_id.wrapping_add(1); - self.entities.insert(new_entity_id); - new_entity_id - } + /// Returns a previously unused entity ID. Use this to "create" a new entity. + pub fn new_entity(&mut self) -> EntityId { + let new_entity_id = self.next_id; + self.next_id = self.next_id.wrapping_add(1); + self.entities.insert(new_entity_id); + new_entity_id + } - /// Removes an entity, making the entity ID unusable with this entity manager as well as - /// removing all of the entity's components. Returns true if the entity was removed, false if - /// the entity ID given did not exist. - pub fn remove_entity(&mut self, entity: EntityId) -> bool { - if !self.has_entity(entity) { - return false; - } + /// Removes an entity, making the entity ID unusable with this entity manager as well as + /// removing all of the entity's components. Returns true if the entity was removed, false if + /// the entity ID given did not exist. + pub fn remove_entity(&mut self, entity: EntityId) -> bool { + if !self.has_entity(entity) { + return false; + } - self.entities.remove(&entity); - for (_, component_store) in self.component_stores.iter_mut() { - component_store.remove(entity); - } - true - } + self.entities.remove(&entity); + for (_, component_store) in self.component_stores.iter_mut() { + component_store.remove(entity); + } + true + } - /// Removes all entities from the entity manager, as well as all of their components. - pub fn remove_all_entities(&mut self) { - self.entities.clear(); - for (_, component_store) in self.component_stores.iter_mut() { - component_store.clear(); - } - } + /// Removes all entities from the entity manager, as well as all of their components. + pub fn remove_all_entities(&mut self) { + self.entities.clear(); + for (_, component_store) in self.component_stores.iter_mut() { + component_store.clear(); + } + } - /// Returns true if the given entity currently has a component of the specified type. - pub fn has_component(&self, entity: EntityId) -> bool { - if !self.has_entity(entity) { - false - } else { - let type_id = TypeId::of::(); - if let Some(component_store) = self.component_stores.get(&type_id) { - component_store.has(entity) - } else { - false - } - } - } + /// Returns true if the given entity currently has a component of the specified type. + pub fn has_component(&self, entity: EntityId) -> bool { + if !self.has_entity(entity) { + false + } else { + let type_id = TypeId::of::(); + if let Some(component_store) = self.component_stores.get(&type_id) { + component_store.has(entity) + } else { + false + } + } + } - /// Adds the given component to the entity manager, associating it with the given entity ID. - /// If this entity already had a component of the same type, that existing component is replaced - /// with this one. Returns true if the component was set to the entity. - pub fn add_component(&mut self, entity: EntityId, component: T) -> bool { - if !self.has_entity(entity) { - false - } else { - if let Some(component_store) = self.get_component_store::() { - component_store.borrow_mut().insert(entity, component); - } else { - self.add_component_store::() - .borrow_mut() - .insert(entity, component); - } - true - } - } + /// Adds the given component to the entity manager, associating it with the given entity ID. + /// If this entity already had a component of the same type, that existing component is replaced + /// with this one. Returns true if the component was set to the entity. + pub fn add_component(&mut self, entity: EntityId, component: T) -> bool { + if !self.has_entity(entity) { + false + } else { + if let Some(component_store) = self.get_component_store::() { + component_store.borrow_mut().insert(entity, component); + } else { + self.add_component_store::() + .borrow_mut() + .insert(entity, component); + } + true + } + } - /// Removes any component of the given type from the specified entity. If the entity had a - /// component of that type and it was removed, returns true. - pub fn remove_component(&mut self, entity: EntityId) -> bool { - if !self.has_entity(entity) { - false - } else { - let type_id = TypeId::of::(); - if let Some(component_store) = self.component_stores.get_mut(&type_id) { - component_store.remove(entity) - } else { - false - } - } - } + /// Removes any component of the given type from the specified entity. If the entity had a + /// component of that type and it was removed, returns true. + pub fn remove_component(&mut self, entity: EntityId) -> bool { + if !self.has_entity(entity) { + false + } else { + let type_id = TypeId::of::(); + if let Some(component_store) = self.component_stores.get_mut(&type_id) { + component_store.remove(entity) + } else { + false + } + } + } - /// Returns a reference to the component store for the given component type. This allows you - /// to get components of the specified type for any number of entities. If there is currently - /// no component store for this type of component, `None` is returned. - #[inline] - pub fn components(&self) -> Option> { - if let Some(component_store) = self.get_component_store() { - Some(component_store.borrow()) - } else { - None - } - } + /// Returns a reference to the component store for the given component type. This allows you + /// to get components of the specified type for any number of entities. If there is currently + /// no component store for this type of component, `None` is returned. + #[inline] + pub fn components(&self) -> Option> { + if let Some(component_store) = self.get_component_store() { + Some(component_store.borrow()) + } else { + None + } + } - /// Returns a reference to the mutable component store for the given component type. This allows - /// you to get and modify components of the specified type for any number of entities. IF there - /// is currently no component store for this type of component, `None` is returned. - /// - /// Note that while technically you can add/remove components using the returned store, you - /// should instead prefer to use [`Entities::add_component`] and [`Entities::remove_component`] - /// instead. - #[inline] - pub fn components_mut(&self) -> Option> { - if let Some(component_store) = self.get_component_store() { - Some(component_store.borrow_mut()) - } else { - None - } - } + /// Returns a reference to the mutable component store for the given component type. This allows + /// you to get and modify components of the specified type for any number of entities. IF there + /// is currently no component store for this type of component, `None` is returned. + /// + /// Note that while technically you can add/remove components using the returned store, you + /// should instead prefer to use [`Entities::add_component`] and [`Entities::remove_component`] + /// instead. + #[inline] + pub fn components_mut(&self) -> Option> { + if let Some(component_store) = self.get_component_store() { + Some(component_store.borrow_mut()) + } else { + None + } + } - /// Initializes a component store for the given component type if one does not exist already. - /// - /// This is technically never needed to be called explicitly (because - /// [`Entities::add_component`] will initialize a missing component store automatically if - /// needed), but is provided as a convenience so that you could, for example, always - /// pre-initialize all of your component stores so that subsequent calls to - /// [`Entities::components`] and [`Entities::components_mut`] are guaranteed to never return - /// `None`. - pub fn init_components(&mut self) { - if self.get_component_store::().is_none() { - self.add_component_store::(); - } - } + /// Initializes a component store for the given component type if one does not exist already. + /// + /// This is technically never needed to be called explicitly (because + /// [`Entities::add_component`] will initialize a missing component store automatically if + /// needed), but is provided as a convenience so that you could, for example, always + /// pre-initialize all of your component stores so that subsequent calls to + /// [`Entities::components`] and [`Entities::components_mut`] are guaranteed to never return + /// `None`. + pub fn init_components(&mut self) { + if self.get_component_store::().is_none() { + self.add_component_store::(); + } + } } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -252,162 +253,162 @@ impl Entities { /// Convenience methods that slightly help the ergonomics of using component stores returned from /// [`Entities::components`]. pub trait ComponentStoreConvenience { - /// Returns the "first" component from the component store along with the entity ID the - /// component is for. This method should only ever be used with components that you know will - /// only ever be attached to one entity (and therefore, the component store for this type of - /// component only has a single entry in it). Otherwise, which component it returns is - /// undefined. - fn single(&self) -> Option<(&EntityId, &T)>; + /// Returns the "first" component from the component store along with the entity ID the + /// component is for. This method should only ever be used with components that you know will + /// only ever be attached to one entity (and therefore, the component store for this type of + /// component only has a single entry in it). Otherwise, which component it returns is + /// undefined. + fn single(&self) -> Option<(&EntityId, &T)>; - /// Returns the component for the given entity, if one exists, otherwise returns `None`. - fn get(&self, k: &EntityId) -> Option<&T>; + /// Returns the component for the given entity, if one exists, otherwise returns `None`. + fn get(&self, k: &EntityId) -> Option<&T>; - /// Returns true if there is a component for the given entity in this store. - fn contains_key(&self, k: &EntityId) -> bool; + /// Returns true if there is a component for the given entity in this store. + fn contains_key(&self, k: &EntityId) -> bool; } pub trait ComponentStoreConvenienceMut { - /// Returns the "first" component from the component store along with the entity ID the - /// component is for as a mutable reference. This method should only ever be used with - /// components that you know will only ever be attached to one entity (and therefore, the - /// component store for this type of component only has a single entry in it). Otherwise, which - /// component it returns is undefined. - fn single_mut(&mut self) -> Option<(&EntityId, &mut T)>; + /// Returns the "first" component from the component store along with the entity ID the + /// component is for as a mutable reference. This method should only ever be used with + /// components that you know will only ever be attached to one entity (and therefore, the + /// component store for this type of component only has a single entry in it). Otherwise, which + /// component it returns is undefined. + fn single_mut(&mut self) -> Option<(&EntityId, &mut T)>; - /// Returns the component for the given entity as a mutable reference if one exists, otherwise - /// returns `None`. - fn get_mut(&mut self, k: &EntityId) -> Option<&mut T>; + /// Returns the component for the given entity as a mutable reference if one exists, otherwise + /// returns `None`. + fn get_mut(&mut self, k: &EntityId) -> Option<&mut T>; - /// Returns true if there is a component for the given entity in this store. - fn contains_key(&mut self, k: &EntityId) -> bool; + /// Returns true if there is a component for the given entity in this store. + fn contains_key(&mut self, k: &EntityId) -> bool; } impl<'a, T: Component> ComponentStoreConvenience for Option> { - fn single(&self) -> Option<(&EntityId, &T)> { - if let Some(components) = self { - if let Some((entity_id, component)) = components.iter().next() { - return Some((entity_id, component)); - } - } - None - } + fn single(&self) -> Option<(&EntityId, &T)> { + if let Some(components) = self { + if let Some((entity_id, component)) = components.iter().next() { + return Some((entity_id, component)); + } + } + None + } - fn get(&self, k: &EntityId) -> Option<&T> { - if let Some(components) = self { - components.get(k) - } else { - None - } - } + fn get(&self, k: &EntityId) -> Option<&T> { + if let Some(components) = self { + components.get(k) + } else { + None + } + } - fn contains_key(&self, k: &EntityId) -> bool { - if let Some(components) = self { - components.contains_key(k) - } else { - false - } - } + fn contains_key(&self, k: &EntityId) -> bool { + if let Some(components) = self { + components.contains_key(k) + } else { + false + } + } } impl<'a, T: Component> ComponentStoreConvenience for Option> { - fn single(&self) -> Option<(&EntityId, &T)> { - if let Some(components) = self { - if let Some((entity_id, component)) = components.iter().next() { - return Some((entity_id, component)); - } - } - None - } + fn single(&self) -> Option<(&EntityId, &T)> { + if let Some(components) = self { + if let Some((entity_id, component)) = components.iter().next() { + return Some((entity_id, component)); + } + } + None + } - fn get(&self, k: &EntityId) -> Option<&T> { - if let Some(components) = self { - components.get(k) - } else { - None - } - } + fn get(&self, k: &EntityId) -> Option<&T> { + if let Some(components) = self { + components.get(k) + } else { + None + } + } - fn contains_key(&self, k: &EntityId) -> bool { - if let Some(components) = self { - components.contains_key(k) - } else { - false - } - } + fn contains_key(&self, k: &EntityId) -> bool { + if let Some(components) = self { + components.contains_key(k) + } else { + false + } + } } impl<'a, T: Component> ComponentStoreConvenienceMut for Option> { - fn single_mut(&mut self) -> Option<(&EntityId, &mut T)> { - if let Some(components) = self { - if let Some((entity_id, component)) = components.iter_mut().next() { - return Some((entity_id, component)); - } - } - None - } + fn single_mut(&mut self) -> Option<(&EntityId, &mut T)> { + if let Some(components) = self { + if let Some((entity_id, component)) = components.iter_mut().next() { + return Some((entity_id, component)); + } + } + None + } - fn get_mut(&mut self, k: &EntityId) -> Option<&mut T> { - if let Some(components) = self { - components.get_mut(k) - } else { - None - } - } + fn get_mut(&mut self, k: &EntityId) -> Option<&mut T> { + if let Some(components) = self { + components.get_mut(k) + } else { + None + } + } - fn contains_key(&mut self, k: &EntityId) -> bool { - if let Some(components) = self { - components.contains_key(k) - } else { - false - } - } + fn contains_key(&mut self, k: &EntityId) -> bool { + if let Some(components) = self { + components.contains_key(k) + } else { + false + } + } } /// Convenience methods that slightly help the ergonomics of using component stores returned from /// [`Entities::components`]. pub trait OptionComponentStore { - /// Returns the total number of components in this component store. This is the same as the - /// number of entities that have a component of this type. - fn len(&self) -> usize; + /// Returns the total number of components in this component store. This is the same as the + /// number of entities that have a component of this type. + fn len(&self) -> usize; - /// Returns true if this store is empty. - fn is_empty(&self) -> bool; + /// Returns true if this store is empty. + fn is_empty(&self) -> bool; } impl<'a, T: Component> OptionComponentStore for Option> { - fn len(&self) -> usize { - if let Some(components) = self { - components.len() - } else { - 0 - } - } + fn len(&self) -> usize { + if let Some(components) = self { + components.len() + } else { + 0 + } + } - fn is_empty(&self) -> bool { - if let Some(components) = self { - components.is_empty() - } else { - true - } - } + fn is_empty(&self) -> bool { + if let Some(components) = self { + components.is_empty() + } else { + true + } + } } impl<'a, T: Component> OptionComponentStore for Option> { - fn len(&self) -> usize { - if let Some(components) = self { - components.len() - } else { - 0 - } - } + fn len(&self) -> usize { + if let Some(components) = self { + components.len() + } else { + 0 + } + } - fn is_empty(&self) -> bool { - if let Some(components) = self { - components.is_empty() - } else { - true - } - } + fn is_empty(&self) -> bool { + if let Some(components) = self { + components.is_empty() + } else { + true + } + } } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -429,418 +430,422 @@ pub type RenderFn = fn(&mut T); /// all of your "update" and "render" component system functions. Both of these types may be the /// same type or different depending on your needs. pub struct ComponentSystems { - update_systems: Vec>, - render_systems: Vec>, + update_systems: Vec>, + render_systems: Vec>, } impl std::fmt::Debug for ComponentSystems { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("ComponentSystems") - .field("update_systems.len()", &self.update_systems.len()) - .field("render_systems.len()", &self.render_systems.len()) - .finish_non_exhaustive() - } + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ComponentSystems") + .field("update_systems.len()", &self.update_systems.len()) + .field("render_systems.len()", &self.render_systems.len()) + .finish_non_exhaustive() + } } impl ComponentSystems { - pub fn new() -> Self { - ComponentSystems { - update_systems: Vec::new(), - render_systems: Vec::new(), - } - } + pub fn new() -> Self { + ComponentSystems { + update_systems: Vec::new(), + render_systems: Vec::new(), + } + } - /// Adds an update component system function to the list of functions that will be called in - /// order whenever [`ComponentSystems::update`] is called. - pub fn add_update_system(&mut self, f: UpdateFn) { - self.update_systems.push(f); - } + /// Adds an update component system function to the list of functions that will be called in + /// order whenever [`ComponentSystems::update`] is called. + pub fn add_update_system(&mut self, f: UpdateFn) { + self.update_systems.push(f); + } - /// Adds a render component system function to the list of functions that will be called in - /// order whenever [`ComponentSystems::render`] is called. - pub fn add_render_system(&mut self, f: RenderFn) { - self.render_systems.push(f); - } + /// Adds a render component system function to the list of functions that will be called in + /// order whenever [`ComponentSystems::render`] is called. + pub fn add_render_system(&mut self, f: RenderFn) { + self.render_systems.push(f); + } - /// Removes all existing update and render component system functions. - pub fn reset(&mut self) { - self.update_systems.clear(); - self.render_systems.clear(); - } + /// Removes all existing update and render component system functions. + pub fn reset(&mut self) { + self.update_systems.clear(); + self.render_systems.clear(); + } - /// Calls each of the update component system functions in the same order that they were added, - /// passing each of them the context argument provided. - pub fn update(&mut self, context: &mut U) { - for f in self.update_systems.iter_mut() { - f(context); - } - } + /// Calls each of the update component system functions in the same order that they were added, + /// passing each of them the context argument provided. + pub fn update(&mut self, context: &mut U) { + for f in self.update_systems.iter_mut() { + f(context); + } + } - /// Calls each of the render component system functions in the same order that they were added, - /// passing each of them the context argument provided. - pub fn render(&mut self, context: &mut R) { - for f in self.render_systems.iter_mut() { - f(context); - } - } + /// Calls each of the render component system functions in the same order that they were added, + /// passing each of them the context argument provided. + pub fn render(&mut self, context: &mut R) { + for f in self.render_systems.iter_mut() { + f(context); + } + } } /////////////////////////////////////////////////////////////////////////////////////////////////// #[cfg(test)] mod tests { - use claim::*; + use claim::*; - use super::*; + use super::*; - #[derive(Debug, Eq, PartialEq, Hash, Clone)] - struct Name(&'static str); - #[derive(Debug, Eq, PartialEq, Hash, Clone)] - struct Position(i32, i32); - #[derive(Debug, Eq, PartialEq, Hash, Clone)] - struct Velocity(i32, i32); - #[derive(Debug, Eq, PartialEq, Hash, Clone)] - struct Health(u32); - #[derive(Debug, Eq, PartialEq, Hash, Clone)] - struct Counter(u32); + #[derive(Debug, Eq, PartialEq, Hash, Clone)] + struct Name(&'static str); - #[test] - fn add_and_remove_entities() { - let mut em = Entities::new(); + #[derive(Debug, Eq, PartialEq, Hash, Clone)] + struct Position(i32, i32); - // add first entity - assert!(!em.has_entity(1)); - let a = em.new_entity(); - assert!(em.has_entity(a)); + #[derive(Debug, Eq, PartialEq, Hash, Clone)] + struct Velocity(i32, i32); - // add second entity with totally different id from first entity - let b = em.new_entity(); - assert!(em.has_entity(a)); - assert!(em.has_entity(b)); - assert_ne!(a, b); + #[derive(Debug, Eq, PartialEq, Hash, Clone)] + struct Health(u32); - // remove first entity - assert!(em.remove_entity(a)); - assert!(!em.has_entity(a)); - assert!(em.has_entity(b)); + #[derive(Debug, Eq, PartialEq, Hash, Clone)] + struct Counter(u32); - // remove second entity - assert!(em.remove_entity(b)); - assert!(!em.has_entity(b)); + #[test] + fn add_and_remove_entities() { + let mut em = Entities::new(); - // removing entities which don't exist shouldn't blow up - assert!(!em.remove_entity(a)); - assert!(!em.remove_entity(b)); + // add first entity + assert!(!em.has_entity(1)); + let a = em.new_entity(); + assert!(em.has_entity(a)); - // add third entity, will not re-use previous entity ids - let c = em.new_entity(); - assert!(em.has_entity(c)); - assert_ne!(a, c); - assert_ne!(b, c); - } + // add second entity with totally different id from first entity + let b = em.new_entity(); + assert!(em.has_entity(a)); + assert!(em.has_entity(b)); + assert_ne!(a, b); - #[test] - fn add_and_remove_entity_components() { - let mut em = Entities::new(); + // remove first entity + assert!(em.remove_entity(a)); + assert!(!em.has_entity(a)); + assert!(em.has_entity(b)); - // create first entity - let a = em.new_entity(); - assert!(em.has_entity(a)); + // remove second entity + assert!(em.remove_entity(b)); + assert!(!em.has_entity(b)); - // add component - assert!(!em.has_component::(a)); - assert!(em.add_component(a, Name("Someone"))); - assert!(em.has_component::(a)); + // removing entities which don't exist shouldn't blow up + assert!(!em.remove_entity(a)); + assert!(!em.remove_entity(b)); - // verify the added component - { - let names = em.components::().unwrap(); - let name_a = names.get(&a).unwrap(); - assert_eq!("Someone", name_a.0); - } + // add third entity, will not re-use previous entity ids + let c = em.new_entity(); + assert!(em.has_entity(c)); + assert_ne!(a, c); + assert_ne!(b, c); + } - // create second entity - let b = em.new_entity(); - assert!(em.has_entity(b)); + #[test] + fn add_and_remove_entity_components() { + let mut em = Entities::new(); - // add component to second entity - assert!(!em.has_component::(b)); - assert_none!(em.components::()); - assert!(em.add_component(b, Position(1, 2))); - assert!(em.has_component::(b)); + // create first entity + let a = em.new_entity(); + assert!(em.has_entity(a)); - // verify the added component - { - let positions = em.components::().unwrap(); - let position_b = positions.get(&b).unwrap(); - assert!(1 == position_b.0 && 2 == position_b.1); - } + // add component + assert!(!em.has_component::(a)); + assert!(em.add_component(a, Name("Someone"))); + assert!(em.has_component::(a)); - // verify current components for both entities are what we expect - assert!(em.has_component::(a)); - assert!(!em.has_component::(b)); - assert!(!em.has_component::(a)); - assert!(em.has_component::(b)); + // verify the added component + { + let names = em.components::().unwrap(); + let name_a = names.get(&a).unwrap(); + assert_eq!("Someone", name_a.0); + } - // add new component to first entity - assert!(em.add_component(a, Position(5, 3))); - assert!(em.has_component::(a)); + // create second entity + let b = em.new_entity(); + assert!(em.has_entity(b)); - // verify both position components for both entities - { - let positions = em.components::().unwrap(); - let position_a = positions.get(&a).unwrap(); - assert!(5 == position_a.0 && 3 == position_a.1); - let position_b = positions.get(&b).unwrap(); - assert!(1 == position_b.0 && 2 == position_b.1); - } + // add component to second entity + assert!(!em.has_component::(b)); + assert_none!(em.components::()); + assert!(em.add_component(b, Position(1, 2))); + assert!(em.has_component::(b)); - // verify current components for both entities are what we expect - assert!(em.has_component::(a)); - assert!(!em.has_component::(b)); - assert!(em.has_component::(a)); - assert!(em.has_component::(b)); + // verify the added component + { + let positions = em.components::().unwrap(); + let position_b = positions.get(&b).unwrap(); + assert!(1 == position_b.0 && 2 == position_b.1); + } - // remove position component from first entity - assert!(em.remove_component::(a)); - assert!(!em.has_component::(a)); + // verify current components for both entities are what we expect + assert!(em.has_component::(a)); + assert!(!em.has_component::(b)); + assert!(!em.has_component::(a)); + assert!(em.has_component::(b)); - // verify current components for both entities are what we expect - assert!(em.has_component::(a)); - assert!(!em.has_component::(b)); - assert!(!em.has_component::(a)); - assert!(em.has_component::(b)); - { - let positions = em.components::().unwrap(); - let position_b = positions.get(&b).unwrap(); - assert!(1 == position_b.0 && 2 == position_b.1); - } - } + // add new component to first entity + assert!(em.add_component(a, Position(5, 3))); + assert!(em.has_component::(a)); - #[test] - fn modify_components() { - let mut em = Entities::new(); + // verify both position components for both entities + { + let positions = em.components::().unwrap(); + let position_a = positions.get(&a).unwrap(); + assert!(5 == position_a.0 && 3 == position_a.1); + let position_b = positions.get(&b).unwrap(); + assert!(1 == position_b.0 && 2 == position_b.1); + } - // create entities - let a = em.new_entity(); - em.add_component(a, Position(10, 20)); - let b = em.new_entity(); - em.add_component(b, Position(17, 5)); + // verify current components for both entities are what we expect + assert!(em.has_component::(a)); + assert!(!em.has_component::(b)); + assert!(em.has_component::(a)); + assert!(em.has_component::(b)); - // change entity positions - { - let mut positions = em.components_mut::().unwrap(); - let position_a = positions.get_mut(&a).unwrap(); - assert_eq!(Position(10, 20), *position_a); - position_a.0 = 13; - position_a.1 = 23; - } + // remove position component from first entity + assert!(em.remove_component::(a)); + assert!(!em.has_component::(a)); - { - let mut positions = em.components_mut::().unwrap(); - let position_b = positions.get_mut(&b).unwrap(); - assert_eq!(Position(17, 5), *position_b); - position_b.0 = 15; - position_b.1 = 8; - } + // verify current components for both entities are what we expect + assert!(em.has_component::(a)); + assert!(!em.has_component::(b)); + assert!(!em.has_component::(a)); + assert!(em.has_component::(b)); + { + let positions = em.components::().unwrap(); + let position_b = positions.get(&b).unwrap(); + assert!(1 == position_b.0 && 2 == position_b.1); + } + } - // verify both entity position components - { - let positions = em.components::().unwrap(); - let position_a = positions.get(&a).unwrap(); - assert_eq!(Position(13, 23), *position_a); - let position_b = positions.get(&b).unwrap(); - assert_eq!(Position(15, 8), *position_b); - } - } + #[test] + fn modify_components() { + let mut em = Entities::new(); - #[test] - fn get_all_components_of_type() { - let mut em = Entities::new(); + // create entities + let a = em.new_entity(); + em.add_component(a, Position(10, 20)); + let b = em.new_entity(); + em.add_component(b, Position(17, 5)); - // create entities - let a = em.new_entity(); - em.add_component(a, Health(20)); - em.add_component(a, Position(10, 20)); - let b = em.new_entity(); - em.add_component(b, Health(30)); - em.add_component(b, Position(17, 5)); + // change entity positions + { + let mut positions = em.components_mut::().unwrap(); + let position_a = positions.get_mut(&a).unwrap(); + assert_eq!(Position(10, 20), *position_a); + position_a.0 = 13; + position_a.1 = 23; + } - // verify initial entity positions - { - let positions = em.components::().unwrap(); - assert_eq!(2, positions.len()); - let positions: HashSet<&Position> = positions.values().collect(); - assert!(positions.contains(&Position(10, 20))); - assert!(positions.contains(&Position(17, 5))); - } + { + let mut positions = em.components_mut::().unwrap(); + let position_b = positions.get_mut(&b).unwrap(); + assert_eq!(Position(17, 5), *position_b); + position_b.0 = 15; + position_b.1 = 8; + } - // modify position components - { - let mut positions = em.components_mut::().unwrap(); - for mut component in positions.values_mut() { - component.0 += 5; - } + // verify both entity position components + { + let positions = em.components::().unwrap(); + let position_a = positions.get(&a).unwrap(); + assert_eq!(Position(13, 23), *position_a); + let position_b = positions.get(&b).unwrap(); + assert_eq!(Position(15, 8), *position_b); + } + } - assert_eq!(Position(15, 20), *positions.get(&a).unwrap()); - assert_eq!(Position(22, 5), *positions.get(&b).unwrap()); - } + #[test] + fn get_all_components_of_type() { + let mut em = Entities::new(); - // modify health components - { - let mut healths = em.components_mut::().unwrap(); - for mut component in healths.values_mut() { - component.0 += 5; - } - assert_eq!(Health(25), *healths.get(&a).unwrap()); - assert_eq!(Health(35), *healths.get(&b).unwrap()); - } - } + // create entities + let a = em.new_entity(); + em.add_component(a, Health(20)); + em.add_component(a, Position(10, 20)); + let b = em.new_entity(); + em.add_component(b, Health(30)); + em.add_component(b, Position(17, 5)); - #[test] - fn get_all_entities_with_component() { - let mut em = Entities::new(); + // verify initial entity positions + { + let positions = em.components::().unwrap(); + assert_eq!(2, positions.len()); + let positions: HashSet<&Position> = positions.values().collect(); + assert!(positions.contains(&Position(10, 20))); + assert!(positions.contains(&Position(17, 5))); + } - // create entities - let a = em.new_entity(); - em.add_component(a, Name("A")); - em.add_component(a, Health(20)); - em.add_component(a, Position(10, 20)); - let b = em.new_entity(); - em.add_component(b, Name("B")); - em.add_component(b, Position(17, 5)); + // modify position components + { + let mut positions = em.components_mut::().unwrap(); + for mut component in positions.values_mut() { + component.0 += 5; + } - // get entities with position components - { - let positions = em.components::().unwrap(); - let entities = positions.keys(); - assert_eq!(2, entities.len()); - let entities: HashSet<&EntityId> = entities.collect(); - assert!(entities.contains(&a)); - assert!(entities.contains(&b)); - } + assert_eq!(Position(15, 20), *positions.get(&a).unwrap()); + assert_eq!(Position(22, 5), *positions.get(&b).unwrap()); + } - // - let names = em.components::().unwrap(); - for (entity, name) in names.iter() { - // just written this way to verify can grab two mutable components at once - // (since this wouldn't be an uncommon way to want to work with an entity) - let mut healths = em.components_mut::().unwrap(); - let mut positions = em.components_mut::().unwrap(); + // modify health components + { + let mut healths = em.components_mut::().unwrap(); + for mut component in healths.values_mut() { + component.0 += 5; + } + assert_eq!(Health(25), *healths.get(&a).unwrap()); + assert_eq!(Health(35), *healths.get(&b).unwrap()); + } + } - let health = healths.get_mut(&entity); - let position = positions.get_mut(&entity); + #[test] + fn get_all_entities_with_component() { + let mut em = Entities::new(); - println!( - "entity {}, health: {:?}, position: {:?}", - name.0, health, position - ); + // create entities + let a = em.new_entity(); + em.add_component(a, Name("A")); + em.add_component(a, Health(20)); + em.add_component(a, Position(10, 20)); + let b = em.new_entity(); + em.add_component(b, Name("B")); + em.add_component(b, Position(17, 5)); - if let Some(mut health) = health { - health.0 += 5; - } - if let Some(mut position) = position { - position.0 += 5; - } - } + // get entities with position components + { + let positions = em.components::().unwrap(); + let entities = positions.keys(); + assert_eq!(2, entities.len()); + let entities: HashSet<&EntityId> = entities.collect(); + assert!(entities.contains(&a)); + assert!(entities.contains(&b)); + } - let positions = em.components::().unwrap(); - assert_eq!(Position(15, 20), *positions.get(&a).unwrap()); - assert_eq!(Position(22, 5), *positions.get(&b).unwrap()); - let healths = em.components::().unwrap(); - assert_eq!(Health(25), *healths.get(&a).unwrap()); - assert!(healths.get(&b).is_none()); - } + // + let names = em.components::().unwrap(); + for (entity, name) in names.iter() { + // just written this way to verify can grab two mutable components at once + // (since this wouldn't be an uncommon way to want to work with an entity) + let mut healths = em.components_mut::().unwrap(); + let mut positions = em.components_mut::().unwrap(); - struct ComponentSystemContext { - pub delta: f32, - pub entities: Entities, - } + let health = healths.get_mut(&entity); + let position = positions.get_mut(&entity); - impl ComponentSystemContext { - pub fn new(entities: Entities) -> Self { - ComponentSystemContext { - delta: 0.0, - entities - } - } - } + println!( + "entity {}, health: {:?}, position: {:?}", + name.0, health, position + ); - fn system_print_entity_positions(context: &mut ComponentSystemContext) { - let positions = context.entities.components::().unwrap(); - for (entity, position) in positions.iter() { - println!("entity {} at x:{}, y:{}", entity, position.0, position.1) - } - } + if let Some(mut health) = health { + health.0 += 5; + } + if let Some(mut position) = position { + position.0 += 5; + } + } - fn system_move_entities_forward(context: &mut ComponentSystemContext) { - let mut positions = context.entities.components_mut::().unwrap(); - let velocities = context.entities.components::().unwrap(); - for (entity, position) in positions.iter_mut() { - if let Some(velocity) = velocities.get(&entity) { - position.0 += velocity.0; - position.1 += velocity.1; - } - } - } + let positions = em.components::().unwrap(); + assert_eq!(Position(15, 20), *positions.get(&a).unwrap()); + assert_eq!(Position(22, 5), *positions.get(&b).unwrap()); + let healths = em.components::().unwrap(); + assert_eq!(Health(25), *healths.get(&a).unwrap()); + assert!(healths.get(&b).is_none()); + } - fn system_increment_counter(context: &mut ComponentSystemContext) { - let mut counters = context.entities.components_mut::().unwrap(); - for (_entity, counter) in counters.iter_mut() { - counter.0 += 1; - } - } + struct ComponentSystemContext { + pub delta: f32, + pub entities: Entities, + } - fn system_print_counter(context: &mut ComponentSystemContext) { - let counters = context.entities.components::().unwrap(); - for (entity, counter) in counters.iter() { - println!("entity {} has counter {}", entity, counter.0); - } - } + impl ComponentSystemContext { + pub fn new(entities: Entities) -> Self { + ComponentSystemContext { + delta: 0.0, + entities, + } + } + } - #[test] - pub fn component_systems() { - let mut context = ComponentSystemContext::new(Entities::new()); + fn system_print_entity_positions(context: &mut ComponentSystemContext) { + let positions = context.entities.components::().unwrap(); + for (entity, position) in positions.iter() { + println!("entity {} at x:{}, y:{}", entity, position.0, position.1) + } + } - // create entities - let a = context.entities.new_entity(); - context.entities.add_component(a, Position(5, 6)); - context.entities.add_component(a, Velocity(1, 1)); - context.entities.add_component(a, Counter(0)); - let b = context.entities.new_entity(); - context.entities.add_component(b, Position(-3, 0)); - context.entities.add_component(b, Velocity(1, 0)); - context.entities.add_component(b, Counter(0)); - let c = context.entities.new_entity(); - context.entities.add_component(c, Position(2, 9)); - context.entities.add_component(c, Counter(0)); + fn system_move_entities_forward(context: &mut ComponentSystemContext) { + let mut positions = context.entities.components_mut::().unwrap(); + let velocities = context.entities.components::().unwrap(); + for (entity, position) in positions.iter_mut() { + if let Some(velocity) = velocities.get(&entity) { + position.0 += velocity.0; + position.1 += velocity.1; + } + } + } - // setup component systems - let mut cs = ComponentSystems::new(); - cs.add_update_system(system_move_entities_forward); - cs.add_update_system(system_increment_counter); - cs.add_render_system(system_print_entity_positions); - cs.add_render_system(system_print_counter); + fn system_increment_counter(context: &mut ComponentSystemContext) { + let mut counters = context.entities.components_mut::().unwrap(); + for (_entity, counter) in counters.iter_mut() { + counter.0 += 1; + } + } - // run some update+render iterations - for _ in 0..5 { - context.delta = 0.0; - cs.update(&mut context); - cs.render(&mut context); - } + fn system_print_counter(context: &mut ComponentSystemContext) { + let counters = context.entities.components::().unwrap(); + for (entity, counter) in counters.iter() { + println!("entity {} has counter {}", entity, counter.0); + } + } - // verify expected entity positions - let positions = context.entities.components::().unwrap(); - let velocities = context.entities.components::().unwrap(); - let counters = context.entities.components::().unwrap(); - assert_eq!(Position(10, 11), *positions.get(&a).unwrap()); - assert_eq!(Velocity(1, 1), *velocities.get(&a).unwrap()); - assert_eq!(Counter(5), *counters.get(&a).unwrap()); - assert_eq!(Position(2, 0), *positions.get(&b).unwrap()); - assert_eq!(Velocity(1, 0), *velocities.get(&b).unwrap()); - assert_eq!(Counter(5), *counters.get(&b).unwrap()); - assert_eq!(Position(2, 9), *positions.get(&c).unwrap()); - assert_eq!(None, velocities.get(&c)); - assert_eq!(Counter(5), *counters.get(&c).unwrap()); - } + #[test] + pub fn component_systems() { + let mut context = ComponentSystemContext::new(Entities::new()); + + // create entities + let a = context.entities.new_entity(); + context.entities.add_component(a, Position(5, 6)); + context.entities.add_component(a, Velocity(1, 1)); + context.entities.add_component(a, Counter(0)); + let b = context.entities.new_entity(); + context.entities.add_component(b, Position(-3, 0)); + context.entities.add_component(b, Velocity(1, 0)); + context.entities.add_component(b, Counter(0)); + let c = context.entities.new_entity(); + context.entities.add_component(c, Position(2, 9)); + context.entities.add_component(c, Counter(0)); + + // setup component systems + let mut cs = ComponentSystems::new(); + cs.add_update_system(system_move_entities_forward); + cs.add_update_system(system_increment_counter); + cs.add_render_system(system_print_entity_positions); + cs.add_render_system(system_print_counter); + + // run some update+render iterations + for _ in 0..5 { + context.delta = 0.0; + cs.update(&mut context); + cs.render(&mut context); + } + + // verify expected entity positions + let positions = context.entities.components::().unwrap(); + let velocities = context.entities.components::().unwrap(); + let counters = context.entities.components::().unwrap(); + assert_eq!(Position(10, 11), *positions.get(&a).unwrap()); + assert_eq!(Velocity(1, 1), *velocities.get(&a).unwrap()); + assert_eq!(Counter(5), *counters.get(&a).unwrap()); + assert_eq!(Position(2, 0), *positions.get(&b).unwrap()); + assert_eq!(Velocity(1, 0), *velocities.get(&b).unwrap()); + assert_eq!(Counter(5), *counters.get(&b).unwrap()); + assert_eq!(Position(2, 9), *positions.get(&c).unwrap()); + assert_eq!(None, velocities.get(&c)); + assert_eq!(Counter(5), *counters.get(&c).unwrap()); + } } diff --git a/libretrogd/src/events/mod.rs b/libretrogd/src/events/mod.rs index 646b6e7..b40df5a 100644 --- a/libretrogd/src/events/mod.rs +++ b/libretrogd/src/events/mod.rs @@ -10,47 +10,47 @@ pub type ListenerFn = fn(event: &EventType, &mut Context /// instance. The `EventType` here should usually be an application-specific "events" enum. #[derive(Clone)] pub struct EventPublisher { - queue: VecDeque, + queue: VecDeque, } impl std::fmt::Debug for EventPublisher { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("EventPublisher") - .field("queue.len()", &self.queue.len()) - .finish_non_exhaustive() - } + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("EventPublisher") + .field("queue.len()", &self.queue.len()) + .finish_non_exhaustive() + } } impl EventPublisher { - pub fn new() -> Self { - EventPublisher { - queue: VecDeque::new(), - } - } + pub fn new() -> Self { + EventPublisher { + queue: VecDeque::new(), + } + } - /// Returns the number of events that have been queued. - #[inline] - pub fn len(&self) -> usize { - self.queue.len() - } + /// Returns the number of events that have been queued. + #[inline] + pub fn len(&self) -> usize { + self.queue.len() + } - /// Clears the current event queue. The events will not be processed/handled. - #[inline] - pub fn clear(&mut self) { - self.queue.clear(); - } + /// Clears the current event queue. The events will not be processed/handled. + #[inline] + pub fn clear(&mut self) { + self.queue.clear(); + } - /// Pushes the given event to the back of the queue. - #[inline] - pub fn queue(&mut self, event: EventType) { - self.queue.push_back(event); - } + /// Pushes the given event to the back of the queue. + #[inline] + pub fn queue(&mut self, event: EventType) { + self.queue.push_back(event); + } - fn take_queue(&mut self, destination: &mut VecDeque) { - destination.clear(); - destination.append(&mut self.queue); - self.clear(); - } + fn take_queue(&mut self, destination: &mut VecDeque) { + destination.clear(); + destination.append(&mut self.queue); + self.clear(); + } } /// A manager for application event listeners/handlers that can dispatch events queued up by a @@ -63,280 +63,278 @@ impl EventPublisher { /// want available in all of your event listener/handler functions. #[derive(Clone)] pub struct EventListeners { - listeners: Vec>, - dispatch_queue: VecDeque, + listeners: Vec>, + dispatch_queue: VecDeque, } impl std::fmt::Debug for EventListeners { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("EventListeners") - .field("listeners.len()", &self.listeners.len()) - .field("dispatch_queue.len()", &self.dispatch_queue.len()) - .finish_non_exhaustive() - } + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("EventListeners") + .field("listeners.len()", &self.listeners.len()) + .field("dispatch_queue.len()", &self.dispatch_queue.len()) + .finish_non_exhaustive() + } } impl EventListeners { - pub fn new() -> Self { - EventListeners { - listeners: Vec::new(), - dispatch_queue: VecDeque::new(), - } - } + pub fn new() -> Self { + EventListeners { + listeners: Vec::new(), + dispatch_queue: VecDeque::new(), + } + } - /// Returns the number of event listeners/handlers registered with this manager. - #[inline] - pub fn len(&self) -> usize { - self.listeners.len() - } + /// Returns the number of event listeners/handlers registered with this manager. + #[inline] + pub fn len(&self) -> usize { + self.listeners.len() + } - /// Unregisters all event listeners/managers previously registered with this manager. - #[inline] - pub fn clear(&mut self) { - self.listeners.clear(); - } + /// Unregisters all event listeners/managers previously registered with this manager. + #[inline] + pub fn clear(&mut self) { + self.listeners.clear(); + } - /// Adds/Registers the given event listener/handler function with this manager so that - /// it will be called during dispatching of events. Returns true if the function was added. - pub fn add(&mut self, listener: ListenerFn) -> bool { - // HACK?: most advice i've seen right now for comparing function pointers suggests doing - // this, but i've also come across comments suggesting there are times where this - // might not be foolproof? (e.g. where generics or lifetimes come into play ... ) - if self.listeners.iter().any(|&l| l as usize == listener as usize) { - false // don't add a duplicate listener - } else { - self.listeners.push(listener); - true - } - } + /// Adds/Registers the given event listener/handler function with this manager so that + /// it will be called during dispatching of events. Returns true if the function was added. + pub fn add(&mut self, listener: ListenerFn) -> bool { + // HACK?: most advice i've seen right now for comparing function pointers suggests doing + // this, but i've also come across comments suggesting there are times where this + // might not be foolproof? (e.g. where generics or lifetimes come into play ... ) + if self.listeners.iter().any(|&l| l as usize == listener as usize) { + false // don't add a duplicate listener + } else { + self.listeners.push(listener); + true + } + } - /// Removes/Unregisters the specified event listener/handler function from this manager. - pub fn remove(&mut self, listener: ListenerFn) -> bool { - let before_size = self.listeners.len(); - // HACK?: comparing function pointers -- see above "HACK?" comment. same concern here. - self.listeners.retain(|&l| l as usize != listener as usize); - // return true if the listener was removed - return before_size != self.listeners.len() - } + /// Removes/Unregisters the specified event listener/handler function from this manager. + pub fn remove(&mut self, listener: ListenerFn) -> bool { + let before_size = self.listeners.len(); + // HACK?: comparing function pointers -- see above "HACK?" comment. same concern here. + self.listeners.retain(|&l| l as usize != listener as usize); + // return true if the listener was removed + return before_size != self.listeners.len(); + } - /// Moves the queue from the given [`EventPublisher`] to this manager in preparation for - /// dispatching the queued events via [`EventListeners::dispatch_queue`]. After calling this, - /// the [`EventPublisher`]'s queue will be empty. - pub fn take_queue_from(&mut self, publisher: &mut EventPublisher) -> usize { - publisher.take_queue(&mut self.dispatch_queue); - self.dispatch_queue.len() - } - - /// Dispatches the previous obtained event queue (via a call to - /// [`EventListeners::take_queue_from`]) to all of the registered event listeners/handlers, - /// passing each of them the given context argument. Not all of the event listeners/handlers - /// will necessarily be called for each event being dispatched depending on which ones handled - /// which events. - pub fn dispatch_queue(&mut self, context: &mut ContextType) { - while let Some(event) = self.dispatch_queue.pop_front() { - for listener in &self.listeners { - if listener(&event, context) { - break; - } - } - } - } + /// Moves the queue from the given [`EventPublisher`] to this manager in preparation for + /// dispatching the queued events via [`EventListeners::dispatch_queue`]. After calling this, + /// the [`EventPublisher`]'s queue will be empty. + pub fn take_queue_from(&mut self, publisher: &mut EventPublisher) -> usize { + publisher.take_queue(&mut self.dispatch_queue); + self.dispatch_queue.len() + } + /// Dispatches the previous obtained event queue (via a call to + /// [`EventListeners::take_queue_from`]) to all of the registered event listeners/handlers, + /// passing each of them the given context argument. Not all of the event listeners/handlers + /// will necessarily be called for each event being dispatched depending on which ones handled + /// which events. + pub fn dispatch_queue(&mut self, context: &mut ContextType) { + while let Some(event) = self.dispatch_queue.pop_front() { + for listener in &self.listeners { + if listener(&event, context) { + break; + } + } + } + } } #[cfg(test)] mod tests { - use super::*; + use super::*; - #[derive(Debug, Eq, PartialEq, Copy, Clone)] - enum TestEvent { - Dummy, - Foobar(i32), - Message(&'static str), - } + #[derive(Debug, Eq, PartialEq, Copy, Clone)] + enum TestEvent { + Dummy, + Foobar(i32), + Message(&'static str), + } - struct DummyContext; + struct DummyContext; - struct TestContext { - pub count: i32, - pub events: Vec, - } + struct TestContext { + pub count: i32, + pub events: Vec, + } - impl TestContext { - pub fn new() -> Self { - TestContext { count: 0, events: Vec::new() } - } - } + impl TestContext { + pub fn new() -> Self { + TestContext { count: 0, events: Vec::new() } + } + } - fn dummy_listener(_event: &TestEvent, _context: &mut DummyContext) -> bool { - false - } + fn dummy_listener(_event: &TestEvent, _context: &mut DummyContext) -> bool { + false + } - fn other_dummy_listener(_event: &TestEvent, _context: &mut DummyContext) -> bool { - false - } + fn other_dummy_listener(_event: &TestEvent, _context: &mut DummyContext) -> bool { + false + } - fn event_logger(event: &TestEvent, context: &mut TestContext) -> bool { - context.events.push(*event); - false - } + fn event_logger(event: &TestEvent, context: &mut TestContext) -> bool { + context.events.push(*event); + false + } - fn event_counter(_event: &TestEvent, context: &mut TestContext) -> bool { - context.count += 1; - false - } + fn event_counter(_event: &TestEvent, context: &mut TestContext) -> bool { + context.count += 1; + false + } - fn message_filter(event: &TestEvent, _context: &mut TestContext) -> bool { - match event { - TestEvent::Message(s) => { - if *s == "filter" { - true // means event was handled, and no subsequent listeners should be called - } else { - false - } - }, - _ => false - } - } + fn message_filter(event: &TestEvent, _context: &mut TestContext) -> bool { + match event { + TestEvent::Message(s) => { + if *s == "filter" { + true // means event was handled, and no subsequent listeners should be called + } else { + false + } + } + _ => false + } + } - #[test] - pub fn adding_and_removing_listeners() { - let mut listeners = EventListeners::::new(); + #[test] + pub fn adding_and_removing_listeners() { + let mut listeners = EventListeners::::new(); - // add and remove - assert_eq!(0, listeners.len()); - assert!(listeners.add(dummy_listener)); - assert_eq!(1, listeners.len()); - assert!(!listeners.add(dummy_listener)); - assert_eq!(1, listeners.len()); - assert!(listeners.remove(dummy_listener)); - assert_eq!(0, listeners.len()); - assert!(!listeners.remove(dummy_listener)); - assert_eq!(0, listeners.len()); + // add and remove + assert_eq!(0, listeners.len()); + assert!(listeners.add(dummy_listener)); + assert_eq!(1, listeners.len()); + assert!(!listeners.add(dummy_listener)); + assert_eq!(1, listeners.len()); + assert!(listeners.remove(dummy_listener)); + assert_eq!(0, listeners.len()); + assert!(!listeners.remove(dummy_listener)); + assert_eq!(0, listeners.len()); - // add and remove multiple - assert!(listeners.add(dummy_listener)); - assert_eq!(1, listeners.len()); - assert!(listeners.add(other_dummy_listener)); - assert_eq!(2, listeners.len()); - assert!(listeners.remove(dummy_listener)); - assert_eq!(1, listeners.len()); - assert!(!listeners.remove(dummy_listener)); - assert_eq!(1, listeners.len()); - assert!(listeners.remove(other_dummy_listener)); - assert_eq!(0, listeners.len()); + // add and remove multiple + assert!(listeners.add(dummy_listener)); + assert_eq!(1, listeners.len()); + assert!(listeners.add(other_dummy_listener)); + assert_eq!(2, listeners.len()); + assert!(listeners.remove(dummy_listener)); + assert_eq!(1, listeners.len()); + assert!(!listeners.remove(dummy_listener)); + assert_eq!(1, listeners.len()); + assert!(listeners.remove(other_dummy_listener)); + assert_eq!(0, listeners.len()); - // clear all - assert!(listeners.add(dummy_listener)); - assert!(listeners.add(other_dummy_listener)); - assert_eq!(2, listeners.len()); - listeners.clear(); - assert_eq!(0, listeners.len()); - } + // clear all + assert!(listeners.add(dummy_listener)); + assert!(listeners.add(other_dummy_listener)); + assert_eq!(2, listeners.len()); + listeners.clear(); + assert_eq!(0, listeners.len()); + } - #[test] - pub fn queueing_events() { - use TestEvent::*; + #[test] + pub fn queueing_events() { + use TestEvent::*; - let mut publisher = EventPublisher::::new(); - assert_eq!(0, publisher.len()); - publisher.queue(Dummy); - assert_eq!(1, publisher.len()); - publisher.queue(Foobar(1)); - assert_eq!(2, publisher.len()); - publisher.queue(Foobar(2)); - assert_eq!(3, publisher.len()); + let mut publisher = EventPublisher::::new(); + assert_eq!(0, publisher.len()); + publisher.queue(Dummy); + assert_eq!(1, publisher.len()); + publisher.queue(Foobar(1)); + assert_eq!(2, publisher.len()); + publisher.queue(Foobar(2)); + assert_eq!(3, publisher.len()); - let mut queue = VecDeque::::new(); - publisher.take_queue(&mut queue); - assert_eq!(0, publisher.len()); - assert_eq!(Dummy, queue.pop_front().unwrap()); - assert_eq!(Foobar(1), queue.pop_front().unwrap()); - assert_eq!(Foobar(2), queue.pop_front().unwrap()); - assert!(queue.pop_front().is_none()); + let mut queue = VecDeque::::new(); + publisher.take_queue(&mut queue); + assert_eq!(0, publisher.len()); + assert_eq!(Dummy, queue.pop_front().unwrap()); + assert_eq!(Foobar(1), queue.pop_front().unwrap()); + assert_eq!(Foobar(2), queue.pop_front().unwrap()); + assert!(queue.pop_front().is_none()); - publisher.queue(Dummy); - assert_eq!(1, publisher.len()); - publisher.clear(); - assert_eq!(0, publisher.len()); - let mut queue = VecDeque::::new(); - publisher.take_queue(&mut queue); - assert_eq!(0, publisher.len()); - assert_eq!(0, queue.len()); - } + publisher.queue(Dummy); + assert_eq!(1, publisher.len()); + publisher.clear(); + assert_eq!(0, publisher.len()); + let mut queue = VecDeque::::new(); + publisher.take_queue(&mut queue); + assert_eq!(0, publisher.len()); + assert_eq!(0, queue.len()); + } - #[test] - pub fn listeners_receive_events() { - use TestEvent::*; + #[test] + pub fn listeners_receive_events() { + use TestEvent::*; - let mut listeners = EventListeners::::new(); - assert!(listeners.add(event_logger)); + let mut listeners = EventListeners::::new(); + assert!(listeners.add(event_logger)); - let mut publisher = EventPublisher::::new(); - publisher.queue(Dummy); - publisher.queue(Foobar(1)); - publisher.queue(Dummy); - publisher.queue(Foobar(42)); - assert_eq!(4, listeners.take_queue_from(&mut publisher)); + let mut publisher = EventPublisher::::new(); + publisher.queue(Dummy); + publisher.queue(Foobar(1)); + publisher.queue(Dummy); + publisher.queue(Foobar(42)); + assert_eq!(4, listeners.take_queue_from(&mut publisher)); - let mut context = TestContext::new(); - assert!(context.events.is_empty()); - assert_eq!(0, context.count); - listeners.dispatch_queue(&mut context); - assert!(!context.events.is_empty()); - assert_eq!(0, context.count); - assert_eq!( - vec![Dummy, Foobar(1), Dummy, Foobar(42)], - context.events - ); + let mut context = TestContext::new(); + assert!(context.events.is_empty()); + assert_eq!(0, context.count); + listeners.dispatch_queue(&mut context); + assert!(!context.events.is_empty()); + assert_eq!(0, context.count); + assert_eq!( + vec![Dummy, Foobar(1), Dummy, Foobar(42)], + context.events + ); - let mut context = TestContext::new(); - assert!(context.events.is_empty()); - assert_eq!(0, context.count); - listeners.dispatch_queue(&mut context); - assert!(context.events.is_empty()); + let mut context = TestContext::new(); + assert!(context.events.is_empty()); + assert_eq!(0, context.count); + listeners.dispatch_queue(&mut context); + assert!(context.events.is_empty()); - assert!(listeners.add(event_counter)); - publisher.queue(Foobar(10)); - publisher.queue(Foobar(20)); - publisher.queue(Dummy); - listeners.take_queue_from(&mut publisher); - let mut context = TestContext::new(); - listeners.dispatch_queue(&mut context); - assert!(!context.events.is_empty()); - assert_eq!(3, context.count); - assert_eq!( - vec![Foobar(10), Foobar(20), Dummy], - context.events - ); - } + assert!(listeners.add(event_counter)); + publisher.queue(Foobar(10)); + publisher.queue(Foobar(20)); + publisher.queue(Dummy); + listeners.take_queue_from(&mut publisher); + let mut context = TestContext::new(); + listeners.dispatch_queue(&mut context); + assert!(!context.events.is_empty()); + assert_eq!(3, context.count); + assert_eq!( + vec![Foobar(10), Foobar(20), Dummy], + context.events + ); + } - #[test] - pub fn listener_filtering() { - use TestEvent::*; + #[test] + pub fn listener_filtering() { + use TestEvent::*; - let mut listeners = EventListeners::::new(); - assert!(listeners.add(message_filter)); - assert!(listeners.add(event_logger)); - assert!(listeners.add(event_counter)); + let mut listeners = EventListeners::::new(); + assert!(listeners.add(message_filter)); + assert!(listeners.add(event_logger)); + assert!(listeners.add(event_counter)); - let mut publisher = EventPublisher::::new(); - publisher.queue(Message("hello")); - publisher.queue(Dummy); - publisher.queue(Message("filter")); - publisher.queue(Foobar(3)); - assert_eq!(4, listeners.take_queue_from(&mut publisher)); + let mut publisher = EventPublisher::::new(); + publisher.queue(Message("hello")); + publisher.queue(Dummy); + publisher.queue(Message("filter")); + publisher.queue(Foobar(3)); + assert_eq!(4, listeners.take_queue_from(&mut publisher)); - let mut context = TestContext::new(); - assert!(context.events.is_empty()); - assert_eq!(0, context.count); - listeners.dispatch_queue(&mut context); - assert!(!context.events.is_empty()); - assert_eq!(3, context.count); - assert_eq!( - vec![Message("hello"), Dummy, Foobar(3)], - context.events - ); - - } + let mut context = TestContext::new(); + assert!(context.events.is_empty()); + assert_eq!(0, context.count); + listeners.dispatch_queue(&mut context); + assert!(!context.events.is_empty()); + assert_eq!(3, context.count); + assert_eq!( + vec![Message("hello"), Dummy, Foobar(3)], + context.events + ); + } } diff --git a/libretrogd/src/graphics/bitmap/blit.rs b/libretrogd/src/graphics/bitmap/blit.rs index afa2889..24f06c4 100644 --- a/libretrogd/src/graphics/bitmap/blit.rs +++ b/libretrogd/src/graphics/bitmap/blit.rs @@ -5,119 +5,119 @@ use crate::math::*; #[derive(Clone, PartialEq)] pub enum BlitMethod { - /// Solid blit, no transparency or other per-pixel adjustments. - Solid, - /// Same as [BlitMethod::Solid] but the drawn image can also be flipped horizontally - /// and/or vertically. - SolidFlipped { - horizontal_flip: bool, - vertical_flip: bool, - }, - /// Transparent blit, the specified source color pixels are skipped. - Transparent(u8), - /// Same as [BlitMethod::Transparent] but the drawn image can also be flipped horizontally - /// and/or vertically. - TransparentFlipped { - transparent_color: u8, - horizontal_flip: bool, - vertical_flip: bool, - }, - /// Same as [BlitMethod::Transparent] except that the visible pixels on the destination are all - /// drawn using the same color. - TransparentSingle { - transparent_color: u8, - draw_color: u8, - }, - /// Combination of [BlitMethod::TransparentFlipped] and [BlitMethod::TransparentSingle]. - TransparentFlippedSingle { - transparent_color: u8, - horizontal_flip: bool, - vertical_flip: bool, - draw_color: u8, - }, - /// Same as [BlitMethod::Solid] except that the drawn pixels have their color indices offset - /// by the amount given. - SolidOffset(u8), - /// Combination of [BlitMethod::SolidFlipped] and [BlitMethod::SolidOffset]. - SolidFlippedOffset { - horizontal_flip: bool, - vertical_flip: bool, - offset: u8, - }, - /// Same as [BlitMethod::Transparent] except that the drawn pixels have their color indices - /// offset by the amount given. The transparent color check is not affected by the offset and - /// is always treated as an absolute palette color index. - TransparentOffset { transparent_color: u8, offset: u8 }, - /// Combination of [BlitMethod::TransparentFlipped] and [BlitMethod::TransparentOffset]. - TransparentFlippedOffset { - transparent_color: u8, - horizontal_flip: bool, - vertical_flip: bool, - offset: u8, - }, - /// Rotozoom blit, works the same as [BlitMethod::Solid] except that rotation and scaling is - /// performed. - RotoZoom { - angle: f32, - scale_x: f32, - scale_y: f32, - }, - /// Same as [BlitMethod::RotoZoom] except that the specified source color pixels are skipped. - RotoZoomTransparent { - angle: f32, - scale_x: f32, - scale_y: f32, - transparent_color: u8, - }, - /// Same as [BlitMethod::RotoZoom] except that the drawn pixels have their color indices - /// offset by the amount given. - RotoZoomOffset { - angle: f32, - scale_x: f32, - scale_y: f32, - offset: u8, - }, - /// Same as [BlitMethod::RotoZoomTransparent] except that the drawn pixels have their color - /// indices offset by the amount given. The transparent color check is not affected by the - /// offset and is always treated as an absolute palette color index. - RotoZoomTransparentOffset { - angle: f32, - scale_x: f32, - scale_y: f32, - transparent_color: u8, - offset: u8, - }, - SolidBlended { - blend_map: Rc, - }, - SolidFlippedBlended { - horizontal_flip: bool, - vertical_flip: bool, - blend_map: Rc, - }, - TransparentBlended { - transparent_color: u8, - blend_map: Rc, - }, - TransparentFlippedBlended { - transparent_color: u8, - horizontal_flip: bool, - vertical_flip: bool, - blend_map: Rc, - }, - RotoZoomBlended { - angle: f32, - scale_x: f32, - scale_y: f32, - blend_map: Rc, - }, - RotoZoomTransparentBlended { - angle: f32, - scale_x: f32, - scale_y: f32, - transparent_color: u8, - blend_map: Rc, - }, + /// Solid blit, no transparency or other per-pixel adjustments. + Solid, + /// Same as [BlitMethod::Solid] but the drawn image can also be flipped horizontally + /// and/or vertically. + SolidFlipped { + horizontal_flip: bool, + vertical_flip: bool, + }, + /// Transparent blit, the specified source color pixels are skipped. + Transparent(u8), + /// Same as [BlitMethod::Transparent] but the drawn image can also be flipped horizontally + /// and/or vertically. + TransparentFlipped { + transparent_color: u8, + horizontal_flip: bool, + vertical_flip: bool, + }, + /// Same as [BlitMethod::Transparent] except that the visible pixels on the destination are all + /// drawn using the same color. + TransparentSingle { + transparent_color: u8, + draw_color: u8, + }, + /// Combination of [BlitMethod::TransparentFlipped] and [BlitMethod::TransparentSingle]. + TransparentFlippedSingle { + transparent_color: u8, + horizontal_flip: bool, + vertical_flip: bool, + draw_color: u8, + }, + /// Same as [BlitMethod::Solid] except that the drawn pixels have their color indices offset + /// by the amount given. + SolidOffset(u8), + /// Combination of [BlitMethod::SolidFlipped] and [BlitMethod::SolidOffset]. + SolidFlippedOffset { + horizontal_flip: bool, + vertical_flip: bool, + offset: u8, + }, + /// Same as [BlitMethod::Transparent] except that the drawn pixels have their color indices + /// offset by the amount given. The transparent color check is not affected by the offset and + /// is always treated as an absolute palette color index. + TransparentOffset { transparent_color: u8, offset: u8 }, + /// Combination of [BlitMethod::TransparentFlipped] and [BlitMethod::TransparentOffset]. + TransparentFlippedOffset { + transparent_color: u8, + horizontal_flip: bool, + vertical_flip: bool, + offset: u8, + }, + /// Rotozoom blit, works the same as [BlitMethod::Solid] except that rotation and scaling is + /// performed. + RotoZoom { + angle: f32, + scale_x: f32, + scale_y: f32, + }, + /// Same as [BlitMethod::RotoZoom] except that the specified source color pixels are skipped. + RotoZoomTransparent { + angle: f32, + scale_x: f32, + scale_y: f32, + transparent_color: u8, + }, + /// Same as [BlitMethod::RotoZoom] except that the drawn pixels have their color indices + /// offset by the amount given. + RotoZoomOffset { + angle: f32, + scale_x: f32, + scale_y: f32, + offset: u8, + }, + /// Same as [BlitMethod::RotoZoomTransparent] except that the drawn pixels have their color + /// indices offset by the amount given. The transparent color check is not affected by the + /// offset and is always treated as an absolute palette color index. + RotoZoomTransparentOffset { + angle: f32, + scale_x: f32, + scale_y: f32, + transparent_color: u8, + offset: u8, + }, + SolidBlended { + blend_map: Rc, + }, + SolidFlippedBlended { + horizontal_flip: bool, + vertical_flip: bool, + blend_map: Rc, + }, + TransparentBlended { + transparent_color: u8, + blend_map: Rc, + }, + TransparentFlippedBlended { + transparent_color: u8, + horizontal_flip: bool, + vertical_flip: bool, + blend_map: Rc, + }, + RotoZoomBlended { + angle: f32, + scale_x: f32, + scale_y: f32, + blend_map: Rc, + }, + RotoZoomTransparentBlended { + angle: f32, + scale_x: f32, + scale_y: f32, + transparent_color: u8, + blend_map: Rc, + }, } /// Clips the region for a source bitmap to be used in a subsequent blit operation. The source @@ -141,1112 +141,1112 @@ pub enum BlitMethod { /// bitmap, or false if the blit is entirely outside of the destination bitmap (and so no blit /// needs to occur) pub fn clip_blit( - dest_clip_region: &Rect, - src_blit_region: &mut Rect, - dest_x: &mut i32, - dest_y: &mut i32, - horizontal_flip: bool, - vertical_flip: bool, + dest_clip_region: &Rect, + src_blit_region: &mut Rect, + dest_x: &mut i32, + dest_y: &mut i32, + horizontal_flip: bool, + vertical_flip: bool, ) -> bool { - // off the left edge? - if *dest_x < dest_clip_region.x { - // completely off the left edge? - if (*dest_x + src_blit_region.width as i32 - 1) < dest_clip_region.x { - return false; - } + // off the left edge? + if *dest_x < dest_clip_region.x { + // completely off the left edge? + if (*dest_x + src_blit_region.width as i32 - 1) < dest_clip_region.x { + return false; + } - let offset = dest_clip_region.x - *dest_x; - if !horizontal_flip { - src_blit_region.x += offset; - } - src_blit_region.width = (src_blit_region.width as i32 - offset) as u32; - *dest_x = dest_clip_region.x; - } + let offset = dest_clip_region.x - *dest_x; + if !horizontal_flip { + src_blit_region.x += offset; + } + src_blit_region.width = (src_blit_region.width as i32 - offset) as u32; + *dest_x = dest_clip_region.x; + } - // off the right edge? - if *dest_x > dest_clip_region.width as i32 - src_blit_region.width as i32 { - // completely off the right edge? - if *dest_x > dest_clip_region.right() { - return false; - } + // off the right edge? + if *dest_x > dest_clip_region.width as i32 - src_blit_region.width as i32 { + // completely off the right edge? + if *dest_x > dest_clip_region.right() { + return false; + } - let offset = *dest_x + src_blit_region.width as i32 - dest_clip_region.width as i32; - if horizontal_flip { - src_blit_region.x += offset; - } - src_blit_region.width = (src_blit_region.width as i32 - offset) as u32; - } + let offset = *dest_x + src_blit_region.width as i32 - dest_clip_region.width as i32; + if horizontal_flip { + src_blit_region.x += offset; + } + src_blit_region.width = (src_blit_region.width as i32 - offset) as u32; + } - // off the top edge? - if *dest_y < dest_clip_region.y { - // completely off the top edge? - if (*dest_y + src_blit_region.height as i32 - 1) < dest_clip_region.y { - return false; - } + // off the top edge? + if *dest_y < dest_clip_region.y { + // completely off the top edge? + if (*dest_y + src_blit_region.height as i32 - 1) < dest_clip_region.y { + return false; + } - let offset = dest_clip_region.y - *dest_y; - if !vertical_flip { - src_blit_region.y += offset; - } - src_blit_region.height = (src_blit_region.height as i32 - offset) as u32; - *dest_y = dest_clip_region.y; - } + let offset = dest_clip_region.y - *dest_y; + if !vertical_flip { + src_blit_region.y += offset; + } + src_blit_region.height = (src_blit_region.height as i32 - offset) as u32; + *dest_y = dest_clip_region.y; + } - // off the bottom edge? - if *dest_y > dest_clip_region.height as i32 - src_blit_region.height as i32 { - // completely off the bottom edge? - if *dest_y > dest_clip_region.bottom() { - return false; - } + // off the bottom edge? + if *dest_y > dest_clip_region.height as i32 - src_blit_region.height as i32 { + // completely off the bottom edge? + if *dest_y > dest_clip_region.bottom() { + return false; + } - let offset = *dest_y + src_blit_region.height as i32 - dest_clip_region.height as i32; - if vertical_flip { - src_blit_region.y += offset; - } - src_blit_region.height = (src_blit_region.height as i32 - offset) as u32; - } + let offset = *dest_y + src_blit_region.height as i32 - dest_clip_region.height as i32; + if vertical_flip { + src_blit_region.y += offset; + } + src_blit_region.height = (src_blit_region.height as i32 - offset) as u32; + } - true + true } #[inline] fn get_flipped_blit_properties( - src: &Bitmap, - src_region: &Rect, - horizontal_flip: bool, - vertical_flip: bool, + src: &Bitmap, + src_region: &Rect, + horizontal_flip: bool, + vertical_flip: bool, ) -> (isize, i32, i32, isize) { - let x_inc; - let src_start_x; - let src_start_y; - let src_next_row_inc; + let x_inc; + let src_start_x; + let src_start_y; + let src_next_row_inc; - if !horizontal_flip && !vertical_flip { - x_inc = 1; - src_start_x = src_region.x; - src_start_y = src_region.y; - src_next_row_inc = (src.width - src_region.width) as isize; - } else if horizontal_flip && !vertical_flip { - x_inc = -1; - src_start_x = src_region.right(); - src_start_y = src_region.y; - src_next_row_inc = (src.width + src_region.width) as isize; - } else if !horizontal_flip && vertical_flip { - x_inc = 1; - src_start_x = src_region.x; - src_start_y = src_region.bottom(); - src_next_row_inc = -((src.width + src_region.width) as isize); - } else { - x_inc = -1; - src_start_x = src_region.right(); - src_start_y = src_region.bottom(); - src_next_row_inc = -((src.width - src_region.width) as isize); - } + if !horizontal_flip && !vertical_flip { + x_inc = 1; + src_start_x = src_region.x; + src_start_y = src_region.y; + src_next_row_inc = (src.width - src_region.width) as isize; + } else if horizontal_flip && !vertical_flip { + x_inc = -1; + src_start_x = src_region.right(); + src_start_y = src_region.y; + src_next_row_inc = (src.width + src_region.width) as isize; + } else if !horizontal_flip && vertical_flip { + x_inc = 1; + src_start_x = src_region.x; + src_start_y = src_region.bottom(); + src_next_row_inc = -((src.width + src_region.width) as isize); + } else { + x_inc = -1; + src_start_x = src_region.right(); + src_start_y = src_region.bottom(); + src_next_row_inc = -((src.width - src_region.width) as isize); + } - (x_inc, src_start_x, src_start_y, src_next_row_inc) + (x_inc, src_start_x, src_start_y, src_next_row_inc) } #[inline] unsafe fn per_pixel_blit( - dest: &mut Bitmap, - src: &Bitmap, - src_region: &Rect, - dest_x: i32, - dest_y: i32, - pixel_fn: impl Fn(*const u8, *mut u8), + dest: &mut Bitmap, + src: &Bitmap, + src_region: &Rect, + dest_x: i32, + dest_y: i32, + pixel_fn: impl Fn(*const u8, *mut u8), ) { - let src_next_row_inc = (src.width - src_region.width) as usize; - let dest_next_row_inc = (dest.width - src_region.width) as usize; - let mut src_pixels = src.pixels_at_ptr_unchecked(src_region.x, src_region.y); - let mut dest_pixels = dest.pixels_at_mut_ptr_unchecked(dest_x, dest_y); + let src_next_row_inc = (src.width - src_region.width) as usize; + let dest_next_row_inc = (dest.width - src_region.width) as usize; + let mut src_pixels = src.pixels_at_ptr_unchecked(src_region.x, src_region.y); + let mut dest_pixels = dest.pixels_at_mut_ptr_unchecked(dest_x, dest_y); - for _ in 0..src_region.height { - for _ in 0..src_region.width { - pixel_fn(src_pixels, dest_pixels); - src_pixels = src_pixels.add(1); - dest_pixels = dest_pixels.add(1); - } + for _ in 0..src_region.height { + for _ in 0..src_region.width { + pixel_fn(src_pixels, dest_pixels); + src_pixels = src_pixels.add(1); + dest_pixels = dest_pixels.add(1); + } - src_pixels = src_pixels.add(src_next_row_inc); - dest_pixels = dest_pixels.add(dest_next_row_inc); - } + src_pixels = src_pixels.add(src_next_row_inc); + dest_pixels = dest_pixels.add(dest_next_row_inc); + } } #[inline] unsafe fn per_pixel_flipped_blit( - dest: &mut Bitmap, - src: &Bitmap, - src_region: &Rect, - dest_x: i32, - dest_y: i32, - horizontal_flip: bool, - vertical_flip: bool, - pixel_fn: impl Fn(*const u8, *mut u8), + dest: &mut Bitmap, + src: &Bitmap, + src_region: &Rect, + dest_x: i32, + dest_y: i32, + horizontal_flip: bool, + vertical_flip: bool, + pixel_fn: impl Fn(*const u8, *mut u8), ) { - let dest_next_row_inc = (dest.width - src_region.width) as usize; - let (x_inc, src_start_x, src_start_y, src_next_row_inc) = - get_flipped_blit_properties(src, src_region, horizontal_flip, vertical_flip); + let dest_next_row_inc = (dest.width - src_region.width) as usize; + let (x_inc, src_start_x, src_start_y, src_next_row_inc) = + get_flipped_blit_properties(src, src_region, horizontal_flip, vertical_flip); - let mut src_pixels = src.pixels_at_ptr_unchecked(src_start_x, src_start_y); - let mut dest_pixels = dest.pixels_at_mut_ptr_unchecked(dest_x, dest_y); + let mut src_pixels = src.pixels_at_ptr_unchecked(src_start_x, src_start_y); + let mut dest_pixels = dest.pixels_at_mut_ptr_unchecked(dest_x, dest_y); - for _ in 0..src_region.height { - for _ in 0..src_region.width { - pixel_fn(src_pixels, dest_pixels); - src_pixels = src_pixels.offset(x_inc); - dest_pixels = dest_pixels.add(1); - } + for _ in 0..src_region.height { + for _ in 0..src_region.width { + pixel_fn(src_pixels, dest_pixels); + src_pixels = src_pixels.offset(x_inc); + dest_pixels = dest_pixels.add(1); + } - src_pixels = src_pixels.offset(src_next_row_inc); - dest_pixels = dest_pixels.add(dest_next_row_inc); - } + src_pixels = src_pixels.offset(src_next_row_inc); + dest_pixels = dest_pixels.add(dest_next_row_inc); + } } #[inline] unsafe fn per_pixel_rotozoom_blit( - dest: &mut Bitmap, - src: &Bitmap, - src_region: &Rect, - dest_x: i32, - dest_y: i32, - angle: f32, - scale_x: f32, - scale_y: f32, - pixel_fn: impl Fn(u8, &mut Bitmap, i32, i32), + dest: &mut Bitmap, + src: &Bitmap, + src_region: &Rect, + dest_x: i32, + dest_y: i32, + angle: f32, + scale_x: f32, + scale_y: f32, + pixel_fn: impl Fn(u8, &mut Bitmap, i32, i32), ) { - let dest_width = src_region.width as f32 * scale_x; - let dest_height = src_region.height as f32 * scale_y; + let dest_width = src_region.width as f32 * scale_x; + let dest_height = src_region.height as f32 * scale_y; - let half_src_width = src_region.width as f32 * 0.5; - let half_src_height = src_region.height as f32 * 0.5; - let half_dest_width = dest_width * 0.5; - let half_dest_height = dest_height * 0.5; + let half_src_width = src_region.width as f32 * 0.5; + let half_src_height = src_region.height as f32 * 0.5; + let half_dest_width = dest_width * 0.5; + let half_dest_height = dest_height * 0.5; - // calculate the destination bitmap axis-aligned bounding box of the region we're drawing to - // based on the source bitmap bounds when rotated and scaled. this is to prevent potentially - // cutting off the corners of the drawn bitmap depending on the exact rotation angle, since - // dest_width and dest_height can only really be used (by themselves) to calculate bounding box - // extents for 90-degree angle rotations. this feels kinda ugly to me, but not sure what other - // clever way to calculate this that there might be (if any). + // calculate the destination bitmap axis-aligned bounding box of the region we're drawing to + // based on the source bitmap bounds when rotated and scaled. this is to prevent potentially + // cutting off the corners of the drawn bitmap depending on the exact rotation angle, since + // dest_width and dest_height can only really be used (by themselves) to calculate bounding box + // extents for 90-degree angle rotations. this feels kinda ugly to me, but not sure what other + // clever way to calculate this that there might be (if any). - let sin = angle.sin(); - let cos = angle.cos(); + let sin = angle.sin(); + let cos = angle.cos(); - let left = -half_dest_width * cos - half_dest_height * sin; - let top = -half_dest_width * sin + half_dest_height * cos; - let right = half_dest_width * cos - half_dest_height * sin; - let bottom = half_dest_width * sin + half_dest_height * cos; + let left = -half_dest_width * cos - half_dest_height * sin; + let top = -half_dest_width * sin + half_dest_height * cos; + let right = half_dest_width * cos - half_dest_height * sin; + let bottom = half_dest_width * sin + half_dest_height * cos; - let (top_left_x, top_left_y) = (left + half_dest_width, top + half_dest_height); - let (top_right_x, top_right_y) = (right + half_dest_width, bottom + half_dest_height); - let (bottom_left_x, bottom_left_y) = (-left + half_dest_width, -top + half_dest_height); - let (bottom_right_x, bottom_right_y) = (-right + half_dest_width, -bottom + half_dest_height); + let (top_left_x, top_left_y) = (left + half_dest_width, top + half_dest_height); + let (top_right_x, top_right_y) = (right + half_dest_width, bottom + half_dest_height); + let (bottom_left_x, bottom_left_y) = (-left + half_dest_width, -top + half_dest_height); + let (bottom_right_x, bottom_right_y) = (-right + half_dest_width, -bottom + half_dest_height); - // HACK: -/+ 1's because this seems to fix some destination area accidental clipping for _some_ - // rotation angles ... ? i guess some other math is probably wrong somewhere or some - // floating point rounding fun perhaps? - let dest_region = Rect::from_coords( - top_left_x.min(bottom_left_x).min(top_right_x).min(bottom_right_x) as i32 - 1, - top_left_y.min(bottom_left_y).min(top_right_y).min(bottom_right_y) as i32 - 1, - top_left_x.max(bottom_left_x).max(top_right_x).max(bottom_right_x) as i32 + 1, - top_left_y.max(bottom_left_y).max(top_right_y).max(bottom_right_y) as i32 + 1 - ); + // HACK: -/+ 1's because this seems to fix some destination area accidental clipping for _some_ + // rotation angles ... ? i guess some other math is probably wrong somewhere or some + // floating point rounding fun perhaps? + let dest_region = Rect::from_coords( + top_left_x.min(bottom_left_x).min(top_right_x).min(bottom_right_x) as i32 - 1, + top_left_y.min(bottom_left_y).min(top_right_y).min(bottom_right_y) as i32 - 1, + top_left_x.max(bottom_left_x).max(top_right_x).max(bottom_right_x) as i32 + 1, + top_left_y.max(bottom_left_y).max(top_right_y).max(bottom_right_y) as i32 + 1, + ); - // now we're ready to draw. we'll be iterating through each pixel on the area we calculated - // just above -- that is (x1,y1)-(x2,y2) -- on the DESTINATION bitmap and for each of these - // x/y coordinates we'll sample the source bitmap after applying a reverse rotation/scale to get - // the equivalent source bitmap x/y pixel coordinate to be drawn. this is to ensure we don't - // end up with any "gap" pixels which would likely result if we instead simply iterated through - // the source bitmap pixels and only drew the resulting pixels. + // now we're ready to draw. we'll be iterating through each pixel on the area we calculated + // just above -- that is (x1,y1)-(x2,y2) -- on the DESTINATION bitmap and for each of these + // x/y coordinates we'll sample the source bitmap after applying a reverse rotation/scale to get + // the equivalent source bitmap x/y pixel coordinate to be drawn. this is to ensure we don't + // end up with any "gap" pixels which would likely result if we instead simply iterated through + // the source bitmap pixels and only drew the resulting pixels. - let sin = -angle.sin(); - let cos = angle.cos(); + let sin = -angle.sin(); + let cos = angle.cos(); - let scale_x = 1.0 / scale_x; - let scale_y = 1.0 / scale_y; + let scale_x = 1.0 / scale_x; + let scale_y = 1.0 / scale_y; - for y in dest_region.y..=dest_region.bottom() { - for x in dest_region.x..=dest_region.right() { - // map the destination bitmap x/y coordinate we're currently at to it's source bitmap - // x/y coordinate by applying a reverse rotation/scale. - // note that for these transformations, we're doing a "weird" thing by utilizing the - // destination bitmap's center point as the origin _except_ for the final post-transform - // offset where we instead use the source bitmap's center point to re-translate the - // coordinates back. this is necessary because of the (potential) scale differences! - let src_x = ((x as f32 - half_dest_width) * cos * scale_x) - ((y as f32 - half_dest_height) * sin * scale_x) + half_src_width; - let src_y = ((x as f32 - half_dest_width) * sin * scale_y) + ((y as f32 - half_dest_height) * cos * scale_y) + half_src_height; + for y in dest_region.y..=dest_region.bottom() { + for x in dest_region.x..=dest_region.right() { + // map the destination bitmap x/y coordinate we're currently at to it's source bitmap + // x/y coordinate by applying a reverse rotation/scale. + // note that for these transformations, we're doing a "weird" thing by utilizing the + // destination bitmap's center point as the origin _except_ for the final post-transform + // offset where we instead use the source bitmap's center point to re-translate the + // coordinates back. this is necessary because of the (potential) scale differences! + let src_x = ((x as f32 - half_dest_width) * cos * scale_x) - ((y as f32 - half_dest_height) * sin * scale_x) + half_src_width; + let src_y = ((x as f32 - half_dest_width) * sin * scale_y) + ((y as f32 - half_dest_height) * cos * scale_y) + half_src_height; - // ensure the source x,y is in bounds, as it very well might not be depending on exactly - // where we are inside the destination area currently. also, we're not interested in - // wrapping of course, since we just want to draw a single instance of this source - // bitmap. - if src_x >= 0.0 && (src_x as i32) < (src_region.width as i32) && src_y >= 0.0 && (src_y as i32) < (src_region.height as i32) { - let pixel = src.get_pixel_unchecked(src_x as i32 + src_region.x, src_y as i32 + src_region.y); + // ensure the source x,y is in bounds, as it very well might not be depending on exactly + // where we are inside the destination area currently. also, we're not interested in + // wrapping of course, since we just want to draw a single instance of this source + // bitmap. + if src_x >= 0.0 && (src_x as i32) < (src_region.width as i32) && src_y >= 0.0 && (src_y as i32) < (src_region.height as i32) { + let pixel = src.get_pixel_unchecked(src_x as i32 + src_region.x, src_y as i32 + src_region.y); - let draw_x = x + dest_x; - let draw_y = y + dest_y; - pixel_fn(pixel, dest, draw_x, draw_y); - } - } - } + let draw_x = x + dest_x; + let draw_y = y + dest_y; + pixel_fn(pixel, dest, draw_x, draw_y); + } + } + } } impl Bitmap { - pub unsafe fn solid_blit(&mut self, src: &Bitmap, src_region: &Rect, dest_x: i32, dest_y: i32) { - let src_row_length = src_region.width as usize; - let src_pitch = src.width as usize; - let dest_pitch = self.width as usize; - let mut src_pixels = src.pixels_at_ptr_unchecked(src_region.x, src_region.y); - let mut dest_pixels = self.pixels_at_mut_ptr_unchecked(dest_x, dest_y); + pub unsafe fn solid_blit(&mut self, src: &Bitmap, src_region: &Rect, dest_x: i32, dest_y: i32) { + let src_row_length = src_region.width as usize; + let src_pitch = src.width as usize; + let dest_pitch = self.width as usize; + let mut src_pixels = src.pixels_at_ptr_unchecked(src_region.x, src_region.y); + let mut dest_pixels = self.pixels_at_mut_ptr_unchecked(dest_x, dest_y); - for _ in 0..src_region.height { - dest_pixels.copy_from(src_pixels, src_row_length); - src_pixels = src_pixels.add(src_pitch); - dest_pixels = dest_pixels.add(dest_pitch); - } - } + for _ in 0..src_region.height { + dest_pixels.copy_from(src_pixels, src_row_length); + src_pixels = src_pixels.add(src_pitch); + dest_pixels = dest_pixels.add(dest_pitch); + } + } - pub unsafe fn solid_blended_blit( - &mut self, - src: &Bitmap, - src_region: &Rect, - dest_x: i32, - dest_y: i32, - blend_map: Rc, - ) { - per_pixel_blit( - self, src, src_region, dest_x, dest_y, - |src_pixels, dest_pixels| { - if let Some(blended_pixel) = blend_map.blend(*src_pixels, *dest_pixels) { - *dest_pixels = blended_pixel; - } else { - *dest_pixels = *src_pixels; - } - } - ); - } + pub unsafe fn solid_blended_blit( + &mut self, + src: &Bitmap, + src_region: &Rect, + dest_x: i32, + dest_y: i32, + blend_map: Rc, + ) { + per_pixel_blit( + self, src, src_region, dest_x, dest_y, + |src_pixels, dest_pixels| { + if let Some(blended_pixel) = blend_map.blend(*src_pixels, *dest_pixels) { + *dest_pixels = blended_pixel; + } else { + *dest_pixels = *src_pixels; + } + }, + ); + } - pub unsafe fn solid_flipped_blit( - &mut self, - src: &Bitmap, - src_region: &Rect, - dest_x: i32, - dest_y: i32, - horizontal_flip: bool, - vertical_flip: bool, - ) { - per_pixel_flipped_blit( - self, src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip, - |src_pixels, dest_pixels| { - *dest_pixels = *src_pixels; - } - ); - } + pub unsafe fn solid_flipped_blit( + &mut self, + src: &Bitmap, + src_region: &Rect, + dest_x: i32, + dest_y: i32, + horizontal_flip: bool, + vertical_flip: bool, + ) { + per_pixel_flipped_blit( + self, src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip, + |src_pixels, dest_pixels| { + *dest_pixels = *src_pixels; + }, + ); + } - pub unsafe fn solid_flipped_blended_blit( - &mut self, - src: &Bitmap, - src_region: &Rect, - dest_x: i32, - dest_y: i32, - horizontal_flip: bool, - vertical_flip: bool, - blend_map: Rc, - ) { - per_pixel_flipped_blit( - self, src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip, - |src_pixels, dest_pixels| { - if let Some(blended_pixel) = blend_map.blend(*src_pixels, *dest_pixels) { - *dest_pixels = blended_pixel; - } else { - *dest_pixels = *src_pixels; - } - } - ); - } + pub unsafe fn solid_flipped_blended_blit( + &mut self, + src: &Bitmap, + src_region: &Rect, + dest_x: i32, + dest_y: i32, + horizontal_flip: bool, + vertical_flip: bool, + blend_map: Rc, + ) { + per_pixel_flipped_blit( + self, src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip, + |src_pixels, dest_pixels| { + if let Some(blended_pixel) = blend_map.blend(*src_pixels, *dest_pixels) { + *dest_pixels = blended_pixel; + } else { + *dest_pixels = *src_pixels; + } + }, + ); + } - pub unsafe fn solid_palette_offset_blit( - &mut self, - src: &Bitmap, - src_region: &Rect, - dest_x: i32, - dest_y: i32, - offset: u8, - ) { - per_pixel_blit( - self, src, src_region, dest_x, dest_y, - |src_pixels, dest_pixels| { - *dest_pixels = (*src_pixels).wrapping_add(offset); - } - ); - } + pub unsafe fn solid_palette_offset_blit( + &mut self, + src: &Bitmap, + src_region: &Rect, + dest_x: i32, + dest_y: i32, + offset: u8, + ) { + per_pixel_blit( + self, src, src_region, dest_x, dest_y, + |src_pixels, dest_pixels| { + *dest_pixels = (*src_pixels).wrapping_add(offset); + }, + ); + } - pub unsafe fn solid_flipped_palette_offset_blit( - &mut self, - src: &Bitmap, - src_region: &Rect, - dest_x: i32, - dest_y: i32, - horizontal_flip: bool, - vertical_flip: bool, - offset: u8, - ) { - per_pixel_flipped_blit( - self, src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip, - |src_pixels, dest_pixels| { - *dest_pixels = (*src_pixels).wrapping_add(offset); - } - ); - } + pub unsafe fn solid_flipped_palette_offset_blit( + &mut self, + src: &Bitmap, + src_region: &Rect, + dest_x: i32, + dest_y: i32, + horizontal_flip: bool, + vertical_flip: bool, + offset: u8, + ) { + per_pixel_flipped_blit( + self, src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip, + |src_pixels, dest_pixels| { + *dest_pixels = (*src_pixels).wrapping_add(offset); + }, + ); + } - pub unsafe fn transparent_blit( - &mut self, - src: &Bitmap, - src_region: &Rect, - dest_x: i32, - dest_y: i32, - transparent_color: u8, - ) { - per_pixel_blit( - self, src, src_region, dest_x, dest_y, - |src_pixels, dest_pixels| { - if *src_pixels != transparent_color { - *dest_pixels = *src_pixels; - } - } - ); - } + pub unsafe fn transparent_blit( + &mut self, + src: &Bitmap, + src_region: &Rect, + dest_x: i32, + dest_y: i32, + transparent_color: u8, + ) { + per_pixel_blit( + self, src, src_region, dest_x, dest_y, + |src_pixels, dest_pixels| { + if *src_pixels != transparent_color { + *dest_pixels = *src_pixels; + } + }, + ); + } - pub unsafe fn transparent_blended_blit( - &mut self, - src: &Bitmap, - src_region: &Rect, - dest_x: i32, - dest_y: i32, - transparent_color: u8, - blend_map: Rc, - ) { - per_pixel_blit( - self, src, src_region, dest_x, dest_y, - |src_pixels, dest_pixels| { - if *src_pixels != transparent_color { - if let Some(blended_pixel) = blend_map.blend(*src_pixels, *dest_pixels) { - *dest_pixels = blended_pixel; - } else { - *dest_pixels = *src_pixels; - } - } - } - ); - } + pub unsafe fn transparent_blended_blit( + &mut self, + src: &Bitmap, + src_region: &Rect, + dest_x: i32, + dest_y: i32, + transparent_color: u8, + blend_map: Rc, + ) { + per_pixel_blit( + self, src, src_region, dest_x, dest_y, + |src_pixels, dest_pixels| { + if *src_pixels != transparent_color { + if let Some(blended_pixel) = blend_map.blend(*src_pixels, *dest_pixels) { + *dest_pixels = blended_pixel; + } else { + *dest_pixels = *src_pixels; + } + } + }, + ); + } - pub unsafe fn transparent_flipped_blit( - &mut self, - src: &Bitmap, - src_region: &Rect, - dest_x: i32, - dest_y: i32, - transparent_color: u8, - horizontal_flip: bool, - vertical_flip: bool, - ) { - per_pixel_flipped_blit( - self, src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip, - |src_pixels, dest_pixels| { - if *src_pixels != transparent_color { - *dest_pixels = *src_pixels; - } - } - ); - } + pub unsafe fn transparent_flipped_blit( + &mut self, + src: &Bitmap, + src_region: &Rect, + dest_x: i32, + dest_y: i32, + transparent_color: u8, + horizontal_flip: bool, + vertical_flip: bool, + ) { + per_pixel_flipped_blit( + self, src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip, + |src_pixels, dest_pixels| { + if *src_pixels != transparent_color { + *dest_pixels = *src_pixels; + } + }, + ); + } - pub unsafe fn transparent_flipped_blended_blit( - &mut self, - src: &Bitmap, - src_region: &Rect, - dest_x: i32, - dest_y: i32, - transparent_color: u8, - horizontal_flip: bool, - vertical_flip: bool, - blend_map: Rc, - ) { - per_pixel_flipped_blit( - self, src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip, - |src_pixels, dest_pixels| { - if *src_pixels != transparent_color { - if let Some(blended_pixel) = blend_map.blend(*src_pixels, *dest_pixels) { - *dest_pixels = blended_pixel; - } else { - *dest_pixels = *src_pixels; - } - } - } - ); - } + pub unsafe fn transparent_flipped_blended_blit( + &mut self, + src: &Bitmap, + src_region: &Rect, + dest_x: i32, + dest_y: i32, + transparent_color: u8, + horizontal_flip: bool, + vertical_flip: bool, + blend_map: Rc, + ) { + per_pixel_flipped_blit( + self, src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip, + |src_pixels, dest_pixels| { + if *src_pixels != transparent_color { + if let Some(blended_pixel) = blend_map.blend(*src_pixels, *dest_pixels) { + *dest_pixels = blended_pixel; + } else { + *dest_pixels = *src_pixels; + } + } + }, + ); + } - pub unsafe fn transparent_palette_offset_blit( - &mut self, - src: &Bitmap, - src_region: &Rect, - dest_x: i32, - dest_y: i32, - transparent_color: u8, - offset: u8, - ) { - per_pixel_blit( - self, src, src_region, dest_x, dest_y, - |src_pixels, dest_pixels| { - if *src_pixels != transparent_color { - *dest_pixels = (*src_pixels).wrapping_add(offset); - } - } - ); - } + pub unsafe fn transparent_palette_offset_blit( + &mut self, + src: &Bitmap, + src_region: &Rect, + dest_x: i32, + dest_y: i32, + transparent_color: u8, + offset: u8, + ) { + per_pixel_blit( + self, src, src_region, dest_x, dest_y, + |src_pixels, dest_pixels| { + if *src_pixels != transparent_color { + *dest_pixels = (*src_pixels).wrapping_add(offset); + } + }, + ); + } - pub unsafe fn transparent_flipped_palette_offset_blit( - &mut self, - src: &Bitmap, - src_region: &Rect, - dest_x: i32, - dest_y: i32, - transparent_color: u8, - horizontal_flip: bool, - vertical_flip: bool, - offset: u8, - ) { - per_pixel_flipped_blit( - self, src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip, - |src_pixels, dest_pixels| { - if *src_pixels != transparent_color { - *dest_pixels = (*src_pixels).wrapping_add(offset); - } - } - ); - } + pub unsafe fn transparent_flipped_palette_offset_blit( + &mut self, + src: &Bitmap, + src_region: &Rect, + dest_x: i32, + dest_y: i32, + transparent_color: u8, + horizontal_flip: bool, + vertical_flip: bool, + offset: u8, + ) { + per_pixel_flipped_blit( + self, src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip, + |src_pixels, dest_pixels| { + if *src_pixels != transparent_color { + *dest_pixels = (*src_pixels).wrapping_add(offset); + } + }, + ); + } - pub unsafe fn transparent_single_color_blit( - &mut self, - src: &Bitmap, - src_region: &Rect, - dest_x: i32, - dest_y: i32, - transparent_color: u8, - draw_color: u8, - ) { - per_pixel_blit( - self, src, src_region, dest_x, dest_y, - |src_pixels, dest_pixels| { - if *src_pixels != transparent_color { - *dest_pixels = draw_color; - } - } - ); - } + pub unsafe fn transparent_single_color_blit( + &mut self, + src: &Bitmap, + src_region: &Rect, + dest_x: i32, + dest_y: i32, + transparent_color: u8, + draw_color: u8, + ) { + per_pixel_blit( + self, src, src_region, dest_x, dest_y, + |src_pixels, dest_pixels| { + if *src_pixels != transparent_color { + *dest_pixels = draw_color; + } + }, + ); + } - pub unsafe fn transparent_flipped_single_color_blit( - &mut self, - src: &Bitmap, - src_region: &Rect, - dest_x: i32, - dest_y: i32, - transparent_color: u8, - horizontal_flip: bool, - vertical_flip: bool, - draw_color: u8, - ) { - per_pixel_flipped_blit( - self, src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip, - |src_pixels, dest_pixels| { - if *src_pixels != transparent_color { - *dest_pixels = draw_color; - } - } - ); - } + pub unsafe fn transparent_flipped_single_color_blit( + &mut self, + src: &Bitmap, + src_region: &Rect, + dest_x: i32, + dest_y: i32, + transparent_color: u8, + horizontal_flip: bool, + vertical_flip: bool, + draw_color: u8, + ) { + per_pixel_flipped_blit( + self, src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip, + |src_pixels, dest_pixels| { + if *src_pixels != transparent_color { + *dest_pixels = draw_color; + } + }, + ); + } - pub unsafe fn rotozoom_blit( - &mut self, - src: &Bitmap, - src_region: &Rect, - dest_x: i32, - dest_y: i32, - angle: f32, - scale_x: f32, - scale_y: f32, - ) { - per_pixel_rotozoom_blit( - self, src, src_region, dest_x, dest_y, angle, scale_x, scale_y, - |src_pixel, dest_bitmap, draw_x, draw_y| { - dest_bitmap.set_pixel(draw_x, draw_y, src_pixel); - //dest_bitmap.set_pixel(draw_x + 1, draw_y, src_pixel); - } - ); - } + pub unsafe fn rotozoom_blit( + &mut self, + src: &Bitmap, + src_region: &Rect, + dest_x: i32, + dest_y: i32, + angle: f32, + scale_x: f32, + scale_y: f32, + ) { + per_pixel_rotozoom_blit( + self, src, src_region, dest_x, dest_y, angle, scale_x, scale_y, + |src_pixel, dest_bitmap, draw_x, draw_y| { + dest_bitmap.set_pixel(draw_x, draw_y, src_pixel); + //dest_bitmap.set_pixel(draw_x + 1, draw_y, src_pixel); + }, + ); + } - pub unsafe fn rotozoom_blended_blit( - &mut self, - src: &Bitmap, - src_region: &Rect, - dest_x: i32, - dest_y: i32, - angle: f32, - scale_x: f32, - scale_y: f32, - blend_map: Rc, - ) { - per_pixel_rotozoom_blit( - self, src, src_region, dest_x, dest_y, angle, scale_x, scale_y, - |src_pixel, dest_bitmap, draw_x, draw_y| { - if let Some(dest_pixel) = dest_bitmap.get_pixel(draw_x, draw_y) { - let draw_pixel = if let Some(blended_pixel) = blend_map.blend(src_pixel, dest_pixel) { - blended_pixel - } else { - src_pixel - }; - dest_bitmap.set_pixel(draw_x, draw_y, draw_pixel); - } - } - ); - } + pub unsafe fn rotozoom_blended_blit( + &mut self, + src: &Bitmap, + src_region: &Rect, + dest_x: i32, + dest_y: i32, + angle: f32, + scale_x: f32, + scale_y: f32, + blend_map: Rc, + ) { + per_pixel_rotozoom_blit( + self, src, src_region, dest_x, dest_y, angle, scale_x, scale_y, + |src_pixel, dest_bitmap, draw_x, draw_y| { + if let Some(dest_pixel) = dest_bitmap.get_pixel(draw_x, draw_y) { + let draw_pixel = if let Some(blended_pixel) = blend_map.blend(src_pixel, dest_pixel) { + blended_pixel + } else { + src_pixel + }; + dest_bitmap.set_pixel(draw_x, draw_y, draw_pixel); + } + }, + ); + } - pub unsafe fn rotozoom_transparent_blit( - &mut self, - src: &Bitmap, - src_region: &Rect, - dest_x: i32, - dest_y: i32, - angle: f32, - scale_x: f32, - scale_y: f32, - transparent_color: u8, - ) { - per_pixel_rotozoom_blit( - self, src, src_region, dest_x, dest_y, angle, scale_x, scale_y, - |src_pixel, dest_bitmap, draw_x, draw_y| { - if transparent_color != src_pixel { - dest_bitmap.set_pixel(draw_x, draw_y, src_pixel); - } - } - ); - } + pub unsafe fn rotozoom_transparent_blit( + &mut self, + src: &Bitmap, + src_region: &Rect, + dest_x: i32, + dest_y: i32, + angle: f32, + scale_x: f32, + scale_y: f32, + transparent_color: u8, + ) { + per_pixel_rotozoom_blit( + self, src, src_region, dest_x, dest_y, angle, scale_x, scale_y, + |src_pixel, dest_bitmap, draw_x, draw_y| { + if transparent_color != src_pixel { + dest_bitmap.set_pixel(draw_x, draw_y, src_pixel); + } + }, + ); + } - pub unsafe fn rotozoom_transparent_blended_blit( - &mut self, - src: &Bitmap, - src_region: &Rect, - dest_x: i32, - dest_y: i32, - angle: f32, - scale_x: f32, - scale_y: f32, - transparent_color: u8, - blend_map: Rc, - ) { - per_pixel_rotozoom_blit( - self, src, src_region, dest_x, dest_y, angle, scale_x, scale_y, - |src_pixel, dest_bitmap, draw_x, draw_y| { - if transparent_color != src_pixel { - if let Some(dest_pixel) = dest_bitmap.get_pixel(draw_x, draw_y) { - let draw_pixel = if let Some(blended_pixel) = blend_map.blend(src_pixel, dest_pixel) { - blended_pixel - } else { - src_pixel - }; - dest_bitmap.set_pixel(draw_x, draw_y, draw_pixel); - } - } - } - ); - } + pub unsafe fn rotozoom_transparent_blended_blit( + &mut self, + src: &Bitmap, + src_region: &Rect, + dest_x: i32, + dest_y: i32, + angle: f32, + scale_x: f32, + scale_y: f32, + transparent_color: u8, + blend_map: Rc, + ) { + per_pixel_rotozoom_blit( + self, src, src_region, dest_x, dest_y, angle, scale_x, scale_y, + |src_pixel, dest_bitmap, draw_x, draw_y| { + if transparent_color != src_pixel { + if let Some(dest_pixel) = dest_bitmap.get_pixel(draw_x, draw_y) { + let draw_pixel = if let Some(blended_pixel) = blend_map.blend(src_pixel, dest_pixel) { + blended_pixel + } else { + src_pixel + }; + dest_bitmap.set_pixel(draw_x, draw_y, draw_pixel); + } + } + }, + ); + } - pub unsafe fn rotozoom_palette_offset_blit( - &mut self, - src: &Bitmap, - src_region: &Rect, - dest_x: i32, - dest_y: i32, - angle: f32, - scale_x: f32, - scale_y: f32, - offset: u8, - ) { - per_pixel_rotozoom_blit( - self, src, src_region, dest_x, dest_y, angle, scale_x, scale_y, - |src_pixel, dest_bitmap, draw_x, draw_y| { - let src_pixel = src_pixel.wrapping_add(offset); - dest_bitmap.set_pixel(draw_x, draw_y, src_pixel); - } - ); - } + pub unsafe fn rotozoom_palette_offset_blit( + &mut self, + src: &Bitmap, + src_region: &Rect, + dest_x: i32, + dest_y: i32, + angle: f32, + scale_x: f32, + scale_y: f32, + offset: u8, + ) { + per_pixel_rotozoom_blit( + self, src, src_region, dest_x, dest_y, angle, scale_x, scale_y, + |src_pixel, dest_bitmap, draw_x, draw_y| { + let src_pixel = src_pixel.wrapping_add(offset); + dest_bitmap.set_pixel(draw_x, draw_y, src_pixel); + }, + ); + } - pub unsafe fn rotozoom_transparent_palette_offset_blit( - &mut self, - src: &Bitmap, - src_region: &Rect, - dest_x: i32, - dest_y: i32, - angle: f32, - scale_x: f32, - scale_y: f32, - transparent_color: u8, - offset: u8, - ) { - per_pixel_rotozoom_blit( - self, src, src_region, dest_x, dest_y, angle, scale_x, scale_y, - |src_pixel, dest_bitmap, draw_x, draw_y| { - if transparent_color != src_pixel { - let src_pixel = src_pixel.wrapping_add(offset); - dest_bitmap.set_pixel(draw_x, draw_y, src_pixel); - } - } - ); - } + pub unsafe fn rotozoom_transparent_palette_offset_blit( + &mut self, + src: &Bitmap, + src_region: &Rect, + dest_x: i32, + dest_y: i32, + angle: f32, + scale_x: f32, + scale_y: f32, + transparent_color: u8, + offset: u8, + ) { + per_pixel_rotozoom_blit( + self, src, src_region, dest_x, dest_y, angle, scale_x, scale_y, + |src_pixel, dest_bitmap, draw_x, draw_y| { + if transparent_color != src_pixel { + let src_pixel = src_pixel.wrapping_add(offset); + dest_bitmap.set_pixel(draw_x, draw_y, src_pixel); + } + }, + ); + } - pub fn blit_region( - &mut self, - method: BlitMethod, - src: &Bitmap, - src_region: &Rect, - mut dest_x: i32, - mut dest_y: i32, - ) { - // make sure the source region is clipped or even valid at all for the source bitmap given - let mut src_region = *src_region; - if !src_region.clamp_to(&src.clip_region) { - return; - } + pub fn blit_region( + &mut self, + method: BlitMethod, + src: &Bitmap, + src_region: &Rect, + mut dest_x: i32, + mut dest_y: i32, + ) { + // make sure the source region is clipped or even valid at all for the source bitmap given + let mut src_region = *src_region; + if !src_region.clamp_to(&src.clip_region) { + return; + } - // some blit methods need to handle clipping a bit differently than others - use BlitMethod::*; - match method { - // rotozoom blits internally clip per-pixel right now ... and regardless, the normal - // clip_blit() function wouldn't handle a rotozoom blit destination region anyway ... - RotoZoom { .. } => {} - RotoZoomBlended { .. } => {} - RotoZoomOffset { .. } => {} - RotoZoomTransparent { .. } => {} - RotoZoomTransparentBlended { .. } => {} - RotoZoomTransparentOffset { .. } => {} + // some blit methods need to handle clipping a bit differently than others + use BlitMethod::*; + match method { + // rotozoom blits internally clip per-pixel right now ... and regardless, the normal + // clip_blit() function wouldn't handle a rotozoom blit destination region anyway ... + RotoZoom { .. } => {} + RotoZoomBlended { .. } => {} + RotoZoomOffset { .. } => {} + RotoZoomTransparent { .. } => {} + RotoZoomTransparentBlended { .. } => {} + RotoZoomTransparentOffset { .. } => {} - // set axis flip arguments - SolidFlipped { horizontal_flip, vertical_flip, .. } | - SolidFlippedBlended { horizontal_flip, vertical_flip, .. } | - SolidFlippedOffset { horizontal_flip, vertical_flip, .. } | - TransparentFlipped { horizontal_flip, vertical_flip, .. } | - TransparentFlippedBlended { horizontal_flip, vertical_flip, .. } | - TransparentFlippedSingle { horizontal_flip, vertical_flip, .. } | - TransparentFlippedOffset { horizontal_flip, vertical_flip, .. } => { - if !clip_blit( - self.clip_region(), - &mut src_region, - &mut dest_x, - &mut dest_y, - horizontal_flip, - vertical_flip, - ) { - return; - } - } + // set axis flip arguments + SolidFlipped { horizontal_flip, vertical_flip, .. } | + SolidFlippedBlended { horizontal_flip, vertical_flip, .. } | + SolidFlippedOffset { horizontal_flip, vertical_flip, .. } | + TransparentFlipped { horizontal_flip, vertical_flip, .. } | + TransparentFlippedBlended { horizontal_flip, vertical_flip, .. } | + TransparentFlippedSingle { horizontal_flip, vertical_flip, .. } | + TransparentFlippedOffset { horizontal_flip, vertical_flip, .. } => { + if !clip_blit( + self.clip_region(), + &mut src_region, + &mut dest_x, + &mut dest_y, + horizontal_flip, + vertical_flip, + ) { + return; + } + } - // otherwise clip like normal! - _ => { - if !clip_blit( - self.clip_region(), - &mut src_region, - &mut dest_x, - &mut dest_y, - false, - false, - ) { - return; - } - } - } + // otherwise clip like normal! + _ => { + if !clip_blit( + self.clip_region(), + &mut src_region, + &mut dest_x, + &mut dest_y, + false, + false, + ) { + return; + } + } + } - unsafe { - self.blit_region_unchecked(method, src, &src_region, dest_x, dest_y); - }; - } + unsafe { + self.blit_region_unchecked(method, src, &src_region, dest_x, dest_y); + }; + } - #[inline] - #[rustfmt::skip] - pub unsafe fn blit_region_unchecked( - &mut self, - method: BlitMethod, - src: &Bitmap, - src_region: &Rect, - dest_x: i32, - dest_y: i32, - ) { - use BlitMethod::*; - match method { - Solid => self.solid_blit(src, src_region, dest_x, dest_y), - SolidFlipped { horizontal_flip, vertical_flip } => { - self.solid_flipped_blit(src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip) - } - SolidOffset(offset) => self.solid_palette_offset_blit(src, src_region, dest_x, dest_y, offset), - SolidFlippedOffset { horizontal_flip, vertical_flip, offset } => { - self.solid_flipped_palette_offset_blit(src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip, offset) - }, - Transparent(transparent_color) => { - self.transparent_blit(src, src_region, dest_x, dest_y, transparent_color) - }, - TransparentFlipped { transparent_color, horizontal_flip, vertical_flip } => { - self.transparent_flipped_blit(src, src_region, dest_x, dest_y, transparent_color, horizontal_flip, vertical_flip) - }, - TransparentOffset { transparent_color, offset } => { - self.transparent_palette_offset_blit(src, src_region, dest_x, dest_y, transparent_color, offset) - }, - TransparentFlippedOffset { transparent_color, horizontal_flip, vertical_flip, offset } => { - self.transparent_flipped_palette_offset_blit(src, src_region, dest_x, dest_y, transparent_color, horizontal_flip, vertical_flip, offset) - }, - TransparentSingle { transparent_color, draw_color } => { - self.transparent_single_color_blit(src, src_region, dest_x, dest_y, transparent_color, draw_color) - }, - TransparentFlippedSingle { transparent_color, horizontal_flip, vertical_flip, draw_color } => { - self.transparent_flipped_single_color_blit(src, src_region, dest_x, dest_y, transparent_color, horizontal_flip, vertical_flip, draw_color) - }, - RotoZoom { angle, scale_x, scale_y } => { - self.rotozoom_blit(src, src_region, dest_x, dest_y, angle, scale_x, scale_y) - }, - RotoZoomOffset { angle, scale_x, scale_y, offset } => { - self.rotozoom_palette_offset_blit(src, src_region, dest_x, dest_y, angle, scale_x, scale_y, offset) - }, - RotoZoomTransparent { angle, scale_x, scale_y, transparent_color } => { - self.rotozoom_transparent_blit(src, src_region, dest_x, dest_y, angle, scale_x, scale_y, transparent_color) - }, - RotoZoomTransparentOffset { angle, scale_x, scale_y, transparent_color, offset } => { - self.rotozoom_transparent_palette_offset_blit(src, src_region, dest_x, dest_y, angle, scale_x, scale_y, transparent_color, offset) - }, - SolidBlended { blend_map } => { - self.solid_blended_blit(src, src_region, dest_x, dest_y, blend_map) - }, - SolidFlippedBlended { horizontal_flip, vertical_flip, blend_map } => { - self.solid_flipped_blended_blit(src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip, blend_map) - }, - TransparentBlended { transparent_color, blend_map } => { - self.transparent_blended_blit(src, src_region, dest_x, dest_y, transparent_color, blend_map) - }, - TransparentFlippedBlended { transparent_color, horizontal_flip, vertical_flip, blend_map } => { - self.transparent_flipped_blended_blit(src, src_region, dest_x, dest_y, transparent_color, horizontal_flip, vertical_flip, blend_map) - }, - RotoZoomBlended { angle, scale_x, scale_y, blend_map } => { - self.rotozoom_blended_blit(src, src_region, dest_x, dest_y, angle, scale_x, scale_y, blend_map) - }, - RotoZoomTransparentBlended { angle, scale_x, scale_y, transparent_color, blend_map } => { - self.rotozoom_transparent_blended_blit(src, src_region, dest_x, dest_y, angle, scale_x, scale_y, transparent_color, blend_map) - } - } - } + #[inline] + #[rustfmt::skip] + pub unsafe fn blit_region_unchecked( + &mut self, + method: BlitMethod, + src: &Bitmap, + src_region: &Rect, + dest_x: i32, + dest_y: i32, + ) { + use BlitMethod::*; + match method { + Solid => self.solid_blit(src, src_region, dest_x, dest_y), + SolidFlipped { horizontal_flip, vertical_flip } => { + self.solid_flipped_blit(src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip) + } + SolidOffset(offset) => self.solid_palette_offset_blit(src, src_region, dest_x, dest_y, offset), + SolidFlippedOffset { horizontal_flip, vertical_flip, offset } => { + self.solid_flipped_palette_offset_blit(src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip, offset) + } + Transparent(transparent_color) => { + self.transparent_blit(src, src_region, dest_x, dest_y, transparent_color) + } + TransparentFlipped { transparent_color, horizontal_flip, vertical_flip } => { + self.transparent_flipped_blit(src, src_region, dest_x, dest_y, transparent_color, horizontal_flip, vertical_flip) + } + TransparentOffset { transparent_color, offset } => { + self.transparent_palette_offset_blit(src, src_region, dest_x, dest_y, transparent_color, offset) + } + TransparentFlippedOffset { transparent_color, horizontal_flip, vertical_flip, offset } => { + self.transparent_flipped_palette_offset_blit(src, src_region, dest_x, dest_y, transparent_color, horizontal_flip, vertical_flip, offset) + } + TransparentSingle { transparent_color, draw_color } => { + self.transparent_single_color_blit(src, src_region, dest_x, dest_y, transparent_color, draw_color) + } + TransparentFlippedSingle { transparent_color, horizontal_flip, vertical_flip, draw_color } => { + self.transparent_flipped_single_color_blit(src, src_region, dest_x, dest_y, transparent_color, horizontal_flip, vertical_flip, draw_color) + } + RotoZoom { angle, scale_x, scale_y } => { + self.rotozoom_blit(src, src_region, dest_x, dest_y, angle, scale_x, scale_y) + } + RotoZoomOffset { angle, scale_x, scale_y, offset } => { + self.rotozoom_palette_offset_blit(src, src_region, dest_x, dest_y, angle, scale_x, scale_y, offset) + } + RotoZoomTransparent { angle, scale_x, scale_y, transparent_color } => { + self.rotozoom_transparent_blit(src, src_region, dest_x, dest_y, angle, scale_x, scale_y, transparent_color) + } + RotoZoomTransparentOffset { angle, scale_x, scale_y, transparent_color, offset } => { + self.rotozoom_transparent_palette_offset_blit(src, src_region, dest_x, dest_y, angle, scale_x, scale_y, transparent_color, offset) + } + SolidBlended { blend_map } => { + self.solid_blended_blit(src, src_region, dest_x, dest_y, blend_map) + } + SolidFlippedBlended { horizontal_flip, vertical_flip, blend_map } => { + self.solid_flipped_blended_blit(src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip, blend_map) + } + TransparentBlended { transparent_color, blend_map } => { + self.transparent_blended_blit(src, src_region, dest_x, dest_y, transparent_color, blend_map) + } + TransparentFlippedBlended { transparent_color, horizontal_flip, vertical_flip, blend_map } => { + self.transparent_flipped_blended_blit(src, src_region, dest_x, dest_y, transparent_color, horizontal_flip, vertical_flip, blend_map) + } + RotoZoomBlended { angle, scale_x, scale_y, blend_map } => { + self.rotozoom_blended_blit(src, src_region, dest_x, dest_y, angle, scale_x, scale_y, blend_map) + } + RotoZoomTransparentBlended { angle, scale_x, scale_y, transparent_color, blend_map } => { + self.rotozoom_transparent_blended_blit(src, src_region, dest_x, dest_y, angle, scale_x, scale_y, transparent_color, blend_map) + } + } + } - #[inline] - pub fn blit(&mut self, method: BlitMethod, src: &Bitmap, x: i32, y: i32) { - let src_region = Rect::new(0, 0, src.width, src.height); - self.blit_region(method, src, &src_region, x, y); - } + #[inline] + pub fn blit(&mut self, method: BlitMethod, src: &Bitmap, x: i32, y: i32) { + let src_region = Rect::new(0, 0, src.width, src.height); + self.blit_region(method, src, &src_region, x, y); + } - #[inline] - pub fn blit_atlas(&mut self, method: BlitMethod, src: &BitmapAtlas, index: usize, x: i32, y: i32) { - if let Some(src_region) = src.get(index) { - self.blit_region(method, src.bitmap(), src_region, x, y); - } - } + #[inline] + pub fn blit_atlas(&mut self, method: BlitMethod, src: &BitmapAtlas, index: usize, x: i32, y: i32) { + if let Some(src_region) = src.get(index) { + self.blit_region(method, src.bitmap(), src_region, x, y); + } + } - #[inline] - pub unsafe fn blit_unchecked(&mut self, method: BlitMethod, src: &Bitmap, x: i32, y: i32) { - let src_region = Rect::new(0, 0, src.width, src.height); - self.blit_region_unchecked(method, src, &src_region, x, y); - } + #[inline] + pub unsafe fn blit_unchecked(&mut self, method: BlitMethod, src: &Bitmap, x: i32, y: i32) { + let src_region = Rect::new(0, 0, src.width, src.height); + self.blit_region_unchecked(method, src, &src_region, x, y); + } - #[inline] - pub unsafe fn blit_atlas_unchecked(&mut self, method: BlitMethod, src: &BitmapAtlas, index: usize, x: i32, y: i32) { - if let Some(src_region) = src.get(index) { - self.blit_region_unchecked(method, src.bitmap(), &src_region, x, y); - } - } + #[inline] + pub unsafe fn blit_atlas_unchecked(&mut self, method: BlitMethod, src: &BitmapAtlas, index: usize, x: i32, y: i32) { + if let Some(src_region) = src.get(index) { + self.blit_region_unchecked(method, src.bitmap(), &src_region, x, y); + } + } } #[cfg(test)] pub mod tests { - use super::*; + use super::*; - #[test] - pub fn clip_blit_regions() { - let dest = Rect::new(0, 0, 320, 240); + #[test] + pub fn clip_blit_regions() { + let dest = Rect::new(0, 0, 320, 240); - let mut src: Rect; - let mut x: i32; - let mut y: i32; + let mut src: Rect; + let mut x: i32; + let mut y: i32; - src = Rect::new(0, 0, 16, 16); - x = 10; - y = 10; - assert!(clip_blit(&dest, &mut src, &mut x, &mut y, false, false)); - assert_eq!(src, Rect::new(0, 0, 16, 16)); - assert_eq!(10, x); - assert_eq!(10, y); + src = Rect::new(0, 0, 16, 16); + x = 10; + y = 10; + assert!(clip_blit(&dest, &mut src, &mut x, &mut y, false, false)); + assert_eq!(src, Rect::new(0, 0, 16, 16)); + assert_eq!(10, x); + assert_eq!(10, y); - // left edge + // left edge - src = Rect::new(0, 0, 16, 16); - x = 0; - y = 10; - assert!(clip_blit(&dest, &mut src, &mut x, &mut y, false, false)); - assert_eq!(src, Rect::new(0, 0, 16, 16)); - assert_eq!(0, x); - assert_eq!(10, y); + src = Rect::new(0, 0, 16, 16); + x = 0; + y = 10; + assert!(clip_blit(&dest, &mut src, &mut x, &mut y, false, false)); + assert_eq!(src, Rect::new(0, 0, 16, 16)); + assert_eq!(0, x); + assert_eq!(10, y); - src = Rect::new(0, 0, 16, 16); - x = -5; - y = 10; - assert!(clip_blit(&dest, &mut src, &mut x, &mut y, false, false)); - assert_eq!(src, Rect::new(5, 0, 11, 16)); - assert_eq!(0, x); - assert_eq!(10, y); + src = Rect::new(0, 0, 16, 16); + x = -5; + y = 10; + assert!(clip_blit(&dest, &mut src, &mut x, &mut y, false, false)); + assert_eq!(src, Rect::new(5, 0, 11, 16)); + assert_eq!(0, x); + assert_eq!(10, y); - src = Rect::new(0, 0, 16, 16); - x = -16; - y = 10; - assert!(!clip_blit(&dest, &mut src, &mut x, &mut y, false, false)); + src = Rect::new(0, 0, 16, 16); + x = -16; + y = 10; + assert!(!clip_blit(&dest, &mut src, &mut x, &mut y, false, false)); - // right edge + // right edge - src = Rect::new(0, 0, 16, 16); - x = 304; - y = 10; - assert!(clip_blit(&dest, &mut src, &mut x, &mut y, false, false)); - assert_eq!(src, Rect::new(0, 0, 16, 16)); - assert_eq!(304, x); - assert_eq!(10, y); + src = Rect::new(0, 0, 16, 16); + x = 304; + y = 10; + assert!(clip_blit(&dest, &mut src, &mut x, &mut y, false, false)); + assert_eq!(src, Rect::new(0, 0, 16, 16)); + assert_eq!(304, x); + assert_eq!(10, y); - src = Rect::new(0, 0, 16, 16); - x = 310; - y = 10; - assert!(clip_blit(&dest, &mut src, &mut x, &mut y, false, false)); - assert_eq!(src, Rect::new(0, 0, 10, 16)); - assert_eq!(310, x); - assert_eq!(10, y); + src = Rect::new(0, 0, 16, 16); + x = 310; + y = 10; + assert!(clip_blit(&dest, &mut src, &mut x, &mut y, false, false)); + assert_eq!(src, Rect::new(0, 0, 10, 16)); + assert_eq!(310, x); + assert_eq!(10, y); - src = Rect::new(0, 0, 16, 16); - x = 320; - y = 10; - assert!(!clip_blit(&dest, &mut src, &mut x, &mut y, false, false)); + src = Rect::new(0, 0, 16, 16); + x = 320; + y = 10; + assert!(!clip_blit(&dest, &mut src, &mut x, &mut y, false, false)); - // top edge + // top edge - src = Rect::new(0, 0, 16, 16); - x = 10; - y = 0; - assert!(clip_blit(&dest, &mut src, &mut x, &mut y, false, false)); - assert_eq!(src, Rect::new(0, 0, 16, 16)); - assert_eq!(10, x); - assert_eq!(0, y); + src = Rect::new(0, 0, 16, 16); + x = 10; + y = 0; + assert!(clip_blit(&dest, &mut src, &mut x, &mut y, false, false)); + assert_eq!(src, Rect::new(0, 0, 16, 16)); + assert_eq!(10, x); + assert_eq!(0, y); - src = Rect::new(0, 0, 16, 16); - x = 10; - y = -5; - assert!(clip_blit(&dest, &mut src, &mut x, &mut y, false, false)); - assert_eq!(src, Rect::new(0, 5, 16, 11)); - assert_eq!(10, x); - assert_eq!(0, y); + src = Rect::new(0, 0, 16, 16); + x = 10; + y = -5; + assert!(clip_blit(&dest, &mut src, &mut x, &mut y, false, false)); + assert_eq!(src, Rect::new(0, 5, 16, 11)); + assert_eq!(10, x); + assert_eq!(0, y); - src = Rect::new(0, 0, 16, 16); - x = 10; - y = -16; - assert!(!clip_blit(&dest, &mut src, &mut x, &mut y, false, false)); + src = Rect::new(0, 0, 16, 16); + x = 10; + y = -16; + assert!(!clip_blit(&dest, &mut src, &mut x, &mut y, false, false)); - // bottom edge + // bottom edge - src = Rect::new(0, 0, 16, 16); - x = 10; - y = 224; - assert!(clip_blit(&dest, &mut src, &mut x, &mut y, false, false)); - assert_eq!(src, Rect::new(0, 0, 16, 16)); - assert_eq!(10, x); - assert_eq!(224, y); + src = Rect::new(0, 0, 16, 16); + x = 10; + y = 224; + assert!(clip_blit(&dest, &mut src, &mut x, &mut y, false, false)); + assert_eq!(src, Rect::new(0, 0, 16, 16)); + assert_eq!(10, x); + assert_eq!(224, y); - src = Rect::new(0, 0, 16, 16); - x = 10; - y = 229; - assert!(clip_blit(&dest, &mut src, &mut x, &mut y, false, false)); - assert_eq!(src, Rect::new(0, 0, 16, 11)); - assert_eq!(10, x); - assert_eq!(229, y); + src = Rect::new(0, 0, 16, 16); + x = 10; + y = 229; + assert!(clip_blit(&dest, &mut src, &mut x, &mut y, false, false)); + assert_eq!(src, Rect::new(0, 0, 16, 11)); + assert_eq!(10, x); + assert_eq!(229, y); - src = Rect::new(0, 0, 16, 16); - x = 10; - y = 240; - assert!(!clip_blit(&dest, &mut src, &mut x, &mut y, false, false)); + src = Rect::new(0, 0, 16, 16); + x = 10; + y = 240; + assert!(!clip_blit(&dest, &mut src, &mut x, &mut y, false, false)); - src = Rect::new(16, 16, 16, 16); - x = -1; - y = 112; - assert!(clip_blit(&dest, &mut src, &mut x, &mut y, false, false)); - assert_eq!(src, Rect::new(17, 16, 15, 16)); - assert_eq!(0, x); - assert_eq!(112, y); - } + src = Rect::new(16, 16, 16, 16); + x = -1; + y = 112; + assert!(clip_blit(&dest, &mut src, &mut x, &mut y, false, false)); + assert_eq!(src, Rect::new(17, 16, 15, 16)); + assert_eq!(0, x); + assert_eq!(112, y); + } - #[test] - pub fn clip_blit_regions_flipped() { - let dest = Rect::new(0, 0, 320, 240); + #[test] + pub fn clip_blit_regions_flipped() { + let dest = Rect::new(0, 0, 320, 240); - let mut src: Rect; - let mut x: i32; - let mut y: i32; + let mut src: Rect; + let mut x: i32; + let mut y: i32; - // left edge + // left edge - src = Rect::new(0, 0, 16, 16); - x = -6; - y = 10; - assert!(clip_blit(&dest, &mut src, &mut x, &mut y, true, false)); - assert_eq!(src, Rect::new(0, 0, 10, 16)); - assert_eq!(0, x); - assert_eq!(10, y); + src = Rect::new(0, 0, 16, 16); + x = -6; + y = 10; + assert!(clip_blit(&dest, &mut src, &mut x, &mut y, true, false)); + assert_eq!(src, Rect::new(0, 0, 10, 16)); + assert_eq!(0, x); + assert_eq!(10, y); - src = Rect::new(0, 0, 16, 16); - x = -6; - y = 10; - assert!(clip_blit(&dest, &mut src, &mut x, &mut y, true, true)); - assert_eq!(src, Rect::new(0, 0, 10, 16)); - assert_eq!(0, x); - assert_eq!(10, y); + src = Rect::new(0, 0, 16, 16); + x = -6; + y = 10; + assert!(clip_blit(&dest, &mut src, &mut x, &mut y, true, true)); + assert_eq!(src, Rect::new(0, 0, 10, 16)); + assert_eq!(0, x); + assert_eq!(10, y); - // right edge + // right edge - src = Rect::new(0, 0, 16, 16); - x = 312; - y = 10; - assert!(clip_blit(&dest, &mut src, &mut x, &mut y, true, false)); - assert_eq!(src, Rect::new(8, 0, 8, 16)); - assert_eq!(312, x); - assert_eq!(10, y); + src = Rect::new(0, 0, 16, 16); + x = 312; + y = 10; + assert!(clip_blit(&dest, &mut src, &mut x, &mut y, true, false)); + assert_eq!(src, Rect::new(8, 0, 8, 16)); + assert_eq!(312, x); + assert_eq!(10, y); - src = Rect::new(0, 0, 16, 16); - x = 312; - y = 10; - assert!(clip_blit(&dest, &mut src, &mut x, &mut y, true, true)); - assert_eq!(src, Rect::new(8, 0, 8, 16)); - assert_eq!(312, x); - assert_eq!(10, y); + src = Rect::new(0, 0, 16, 16); + x = 312; + y = 10; + assert!(clip_blit(&dest, &mut src, &mut x, &mut y, true, true)); + assert_eq!(src, Rect::new(8, 0, 8, 16)); + assert_eq!(312, x); + assert_eq!(10, y); - // top edge + // top edge - src = Rect::new(0, 0, 16, 16); - x = 10; - y = -2; - assert!(clip_blit(&dest, &mut src, &mut x, &mut y, false, true)); - assert_eq!(src, Rect::new(0, 0, 16, 14)); - assert_eq!(10, x); - assert_eq!(0, y); + src = Rect::new(0, 0, 16, 16); + x = 10; + y = -2; + assert!(clip_blit(&dest, &mut src, &mut x, &mut y, false, true)); + assert_eq!(src, Rect::new(0, 0, 16, 14)); + assert_eq!(10, x); + assert_eq!(0, y); - src = Rect::new(0, 0, 16, 16); - x = 10; - y = -2; - assert!(clip_blit(&dest, &mut src, &mut x, &mut y, true, true)); - assert_eq!(src, Rect::new(0, 0, 16, 14)); - assert_eq!(10, x); - assert_eq!(0, y); + src = Rect::new(0, 0, 16, 16); + x = 10; + y = -2; + assert!(clip_blit(&dest, &mut src, &mut x, &mut y, true, true)); + assert_eq!(src, Rect::new(0, 0, 16, 14)); + assert_eq!(10, x); + assert_eq!(0, y); - // bottom edge + // bottom edge - src = Rect::new(0, 0, 16, 16); - x = 10; - y = 235; - assert!(clip_blit(&dest, &mut src, &mut x, &mut y, false, true)); - assert_eq!(src, Rect::new(0, 11, 16, 5)); - assert_eq!(10, x); - assert_eq!(235, y); + src = Rect::new(0, 0, 16, 16); + x = 10; + y = 235; + assert!(clip_blit(&dest, &mut src, &mut x, &mut y, false, true)); + assert_eq!(src, Rect::new(0, 11, 16, 5)); + assert_eq!(10, x); + assert_eq!(235, y); - src = Rect::new(0, 0, 16, 16); - x = 10; - y = 235; - assert!(clip_blit(&dest, &mut src, &mut x, &mut y, true, true)); - assert_eq!(src, Rect::new(0, 11, 16, 5)); - assert_eq!(10, x); - assert_eq!(235, y); + src = Rect::new(0, 0, 16, 16); + x = 10; + y = 235; + assert!(clip_blit(&dest, &mut src, &mut x, &mut y, true, true)); + assert_eq!(src, Rect::new(0, 11, 16, 5)); + assert_eq!(10, x); + assert_eq!(235, y); - // top-left edge + // top-left edge - src = Rect::new(0, 0, 16, 16); - x = -2; - y = -6; - assert!(clip_blit(&dest, &mut src, &mut x, &mut y, true, true)); - assert_eq!(src, Rect::new(0, 0, 14, 10)); - assert_eq!(0, x); - assert_eq!(0, y); + src = Rect::new(0, 0, 16, 16); + x = -2; + y = -6; + assert!(clip_blit(&dest, &mut src, &mut x, &mut y, true, true)); + assert_eq!(src, Rect::new(0, 0, 14, 10)); + assert_eq!(0, x); + assert_eq!(0, y); - // top-right edge + // top-right edge - src = Rect::new(0, 0, 16, 16); - x = 311; - y = -12; - assert!(clip_blit(&dest, &mut src, &mut x, &mut y, true, true)); - assert_eq!(src, Rect::new(7, 0, 9, 4)); - assert_eq!(311, x); - assert_eq!(0, y); + src = Rect::new(0, 0, 16, 16); + x = 311; + y = -12; + assert!(clip_blit(&dest, &mut src, &mut x, &mut y, true, true)); + assert_eq!(src, Rect::new(7, 0, 9, 4)); + assert_eq!(311, x); + assert_eq!(0, y); - // bottom-left edge + // bottom-left edge - src = Rect::new(0, 0, 16, 16); - x = -1; - y = 232; - assert!(clip_blit(&dest, &mut src, &mut x, &mut y, true, true)); - assert_eq!(src, Rect::new(0, 8, 15, 8)); - assert_eq!(0, x); - assert_eq!(232, y); + src = Rect::new(0, 0, 16, 16); + x = -1; + y = 232; + assert!(clip_blit(&dest, &mut src, &mut x, &mut y, true, true)); + assert_eq!(src, Rect::new(0, 8, 15, 8)); + assert_eq!(0, x); + assert_eq!(232, y); - // bottom-right edge + // bottom-right edge - src = Rect::new(0, 0, 16, 16); - x = 314; - y = 238; - assert!(clip_blit(&dest, &mut src, &mut x, &mut y, true, true)); - assert_eq!(src, Rect::new(10, 14, 6, 2)); - assert_eq!(314, x); - assert_eq!(238, y); - } + src = Rect::new(0, 0, 16, 16); + x = 314; + y = 238; + assert!(clip_blit(&dest, &mut src, &mut x, &mut y, true, true)); + assert_eq!(src, Rect::new(10, 14, 6, 2)); + assert_eq!(314, x); + assert_eq!(238, y); + } - #[test] - pub fn clip_blit_regions_large_source() { - let dest = Rect::new(0, 0, 64, 64); + #[test] + pub fn clip_blit_regions_large_source() { + let dest = Rect::new(0, 0, 64, 64); - let mut src: Rect; - let mut x: i32; - let mut y: i32; + let mut src: Rect; + let mut x: i32; + let mut y: i32; - src = Rect::new(0, 0, 128, 128); - x = 0; - y = 0; - assert!(clip_blit(&dest, &mut src, &mut x, &mut y, false, false)); - assert_eq!(src, Rect::new(0, 0, 64, 64)); - assert_eq!(0, x); - assert_eq!(0, y); + src = Rect::new(0, 0, 128, 128); + x = 0; + y = 0; + assert!(clip_blit(&dest, &mut src, &mut x, &mut y, false, false)); + assert_eq!(src, Rect::new(0, 0, 64, 64)); + assert_eq!(0, x); + assert_eq!(0, y); - src = Rect::new(0, 0, 128, 128); - x = -16; - y = -24; - assert!(clip_blit(&dest, &mut src, &mut x, &mut y, false, false)); - assert_eq!(src, Rect::new(16, 24, 64, 64)); - assert_eq!(0, x); - assert_eq!(0, y); + src = Rect::new(0, 0, 128, 128); + x = -16; + y = -24; + assert!(clip_blit(&dest, &mut src, &mut x, &mut y, false, false)); + assert_eq!(src, Rect::new(16, 24, 64, 64)); + assert_eq!(0, x); + assert_eq!(0, y); - src = Rect::new(0, 0, 32, 128); - x = 10; - y = -20; - assert!(clip_blit(&dest, &mut src, &mut x, &mut y, false, false)); - assert_eq!(src, Rect::new(0, 20, 32, 64)); - assert_eq!(10, x); - assert_eq!(0, y); + src = Rect::new(0, 0, 32, 128); + x = 10; + y = -20; + assert!(clip_blit(&dest, &mut src, &mut x, &mut y, false, false)); + assert_eq!(src, Rect::new(0, 20, 32, 64)); + assert_eq!(10, x); + assert_eq!(0, y); - src = Rect::new(0, 0, 128, 32); - x = -20; - y = 10; - assert!(clip_blit(&dest, &mut src, &mut x, &mut y, false, false)); - assert_eq!(src, Rect::new(20, 0, 64, 32)); - assert_eq!(0, x); - assert_eq!(10, y); - } + src = Rect::new(0, 0, 128, 32); + x = -20; + y = 10; + assert!(clip_blit(&dest, &mut src, &mut x, &mut y, false, false)); + assert_eq!(src, Rect::new(20, 0, 64, 32)); + assert_eq!(0, x); + assert_eq!(10, y); + } } diff --git a/libretrogd/src/graphics/bitmap/gif.rs b/libretrogd/src/graphics/bitmap/gif.rs index 0bba295..0f13952 100644 --- a/libretrogd/src/graphics/bitmap/gif.rs +++ b/libretrogd/src/graphics/bitmap/gif.rs @@ -11,132 +11,132 @@ use crate::utils::lzwgif::*; const BITS_FOR_256_COLORS: u32 = 7; // formula is `2 ^ (bits + 1) = num_colors` fn bits_to_num_colors(bits: u32) -> u32 { - 1_u32.wrapping_shl(bits + 1) + 1_u32.wrapping_shl(bits + 1) } fn read_raw_sub_block_data(reader: &mut T) -> Result, GifError> { - let mut data = Vec::new(); - let mut count = reader.read_u8()?; - while count > 0 { - let mut sub_block = vec![0u8; count as usize]; - reader.read_exact(&mut sub_block)?; - data.append(&mut sub_block); - // read next sub block data size (or 0 if this is the end) - count = reader.read_u8()?; - } - Ok(data.into_boxed_slice()) + let mut data = Vec::new(); + let mut count = reader.read_u8()?; + while count > 0 { + let mut sub_block = vec![0u8; count as usize]; + reader.read_exact(&mut sub_block)?; + data.append(&mut sub_block); + // read next sub block data size (or 0 if this is the end) + count = reader.read_u8()?; + } + Ok(data.into_boxed_slice()) } fn write_raw_sub_block_data(data: &[u8], writer: &mut T) -> Result<(), GifError> { - let mut bytes_left = data.len(); - let mut pos = 0; - while bytes_left > 0 { - let sub_block_length = if bytes_left >= 255 { 255 } else { bytes_left }; - writer.write_u8(sub_block_length as u8)?; - let sub_block = &data[pos..sub_block_length]; - writer.write_all(sub_block)?; - pos += sub_block_length; - bytes_left -= sub_block_length; - } - // terminator (sub block of zero length) - writer.write_u8(0)?; - Ok(()) + let mut bytes_left = data.len(); + let mut pos = 0; + while bytes_left > 0 { + let sub_block_length = if bytes_left >= 255 { 255 } else { bytes_left }; + writer.write_u8(sub_block_length as u8)?; + let sub_block = &data[pos..sub_block_length]; + writer.write_all(sub_block)?; + pos += sub_block_length; + bytes_left -= sub_block_length; + } + // terminator (sub block of zero length) + writer.write_u8(0)?; + Ok(()) } #[derive(Error, Debug)] pub enum GifError { - #[error("Bad or unsupported GIF file: {0}")] - BadFile(String), + #[error("Bad or unsupported GIF file: {0}")] + BadFile(String), - #[error("GIF palette data error")] - BadPalette(#[from] PaletteError), + #[error("GIF palette data error")] + BadPalette(#[from] PaletteError), - #[error("Unknown extension block: {0}")] - UnknownExtension(u8), + #[error("Unknown extension block: {0}")] + UnknownExtension(u8), - #[error("LZW encoding/decoding error")] - LzwError(#[from] LzwError), + #[error("LZW encoding/decoding error")] + LzwError(#[from] LzwError), - #[error("")] - IOError(#[from] std::io::Error), + #[error("")] + IOError(#[from] std::io::Error), } pub enum GifSettings { - Default, - TransparentColor(u8), + Default, + TransparentColor(u8), } #[derive(Debug, Copy, Clone)] struct GifHeader { - signature: [u8; 3], - version: [u8; 3], - screen_width: u16, - screen_height: u16, - flags: u8, - background_color: u8, - aspect_ratio: u8, + signature: [u8; 3], + version: [u8; 3], + screen_width: u16, + screen_height: u16, + flags: u8, + background_color: u8, + aspect_ratio: u8, } #[allow(dead_code)] impl GifHeader { - pub fn has_global_color_table(&self) -> bool { - self.flags & 0b10000000 != 0 - } + pub fn has_global_color_table(&self) -> bool { + self.flags & 0b10000000 != 0 + } - pub fn set_global_color_table(&mut self, value: bool) { - self.flags |= (value as u8).wrapping_shl(7); - } + pub fn set_global_color_table(&mut self, value: bool) { + self.flags |= (value as u8).wrapping_shl(7); + } - pub fn color_resolution_bits(&self) -> u8 { - (self.flags & 0b01110000).wrapping_shr(4) - } + pub fn color_resolution_bits(&self) -> u8 { + (self.flags & 0b01110000).wrapping_shr(4) + } - pub fn set_color_resolution_bits(&mut self, value: u8) { - self.flags |= (value & 0b111).wrapping_shl(4); - } + pub fn set_color_resolution_bits(&mut self, value: u8) { + self.flags |= (value & 0b111).wrapping_shl(4); + } - pub fn is_color_table_entries_sorted(&self) -> bool { - self.flags & 0b00001000 != 0 - } + pub fn is_color_table_entries_sorted(&self) -> bool { + self.flags & 0b00001000 != 0 + } - pub fn set_color_table_entries_sorted(&mut self, value: bool) { - self.flags |= (value as u8).wrapping_shl(3); - } + pub fn set_color_table_entries_sorted(&mut self, value: bool) { + self.flags |= (value as u8).wrapping_shl(3); + } - pub fn global_color_table_bits(&self) -> u8 { - self.flags & 0b00000111 - } + pub fn global_color_table_bits(&self) -> u8 { + self.flags & 0b00000111 + } - pub fn set_global_color_table_bits(&mut self, value: u8) { - self.flags |= value & 0b111; - } + pub fn set_global_color_table_bits(&mut self, value: u8) { + self.flags |= value & 0b111; + } - pub fn read(reader: &mut T) -> Result { - let mut signature = [0u8; 3]; - reader.read_exact(&mut signature)?; - let mut version = [0u8; 3]; - reader.read_exact(&mut version)?; - Ok(GifHeader { - signature, - version, - screen_width: reader.read_u16::()?, - screen_height: reader.read_u16::()?, - flags: reader.read_u8()?, - background_color: reader.read_u8()?, - aspect_ratio: reader.read_u8()?, - }) - } + pub fn read(reader: &mut T) -> Result { + let mut signature = [0u8; 3]; + reader.read_exact(&mut signature)?; + let mut version = [0u8; 3]; + reader.read_exact(&mut version)?; + Ok(GifHeader { + signature, + version, + screen_width: reader.read_u16::()?, + screen_height: reader.read_u16::()?, + flags: reader.read_u8()?, + background_color: reader.read_u8()?, + aspect_ratio: reader.read_u8()?, + }) + } - pub fn write(&self, writer: &mut T) -> Result<(), GifError> { - writer.write_all(&self.signature)?; - writer.write_all(&self.version)?; - writer.write_u16::(self.screen_width)?; - writer.write_u16::(self.screen_height)?; - writer.write_u8(self.flags)?; - writer.write_u8(self.background_color)?; - writer.write_u8(self.aspect_ratio)?; - Ok(()) - } + pub fn write(&self, writer: &mut T) -> Result<(), GifError> { + writer.write_all(&self.signature)?; + writer.write_all(&self.version)?; + writer.write_u16::(self.screen_width)?; + writer.write_u16::(self.screen_height)?; + writer.write_u8(self.flags)?; + writer.write_u8(self.background_color)?; + writer.write_u8(self.aspect_ratio)?; + Ok(()) + } } const GIF_TRAILER: u8 = 0x3b; @@ -145,481 +145,480 @@ const IMAGE_DESCRIPTOR_SEPARATOR: u8 = 0x2c; #[derive(Debug, Copy, Clone, Eq, PartialEq)] enum GifExtensionLabel { - GraphicControl = 0xf9, - PlainText = 0x01, - Application = 0xff, - Comment = 0xfe, + GraphicControl = 0xf9, + PlainText = 0x01, + Application = 0xff, + Comment = 0xfe, } impl GifExtensionLabel { - pub fn from(value: u8) -> Result { - use GifExtensionLabel::*; - match value { - 0xf9 => Ok(GraphicControl), - 0x01 => Ok(PlainText), - 0xff => Ok(Application), - 0xfe => Ok(Comment), - _ => Err(GifError::UnknownExtension(value)) - } - } + pub fn from(value: u8) -> Result { + use GifExtensionLabel::*; + match value { + 0xf9 => Ok(GraphicControl), + 0x01 => Ok(PlainText), + 0xff => Ok(Application), + 0xfe => Ok(Comment), + _ => Err(GifError::UnknownExtension(value)) + } + } } #[derive(Debug, Copy, Clone)] struct GraphicControlExtension { - block_size: u8, - flags: u8, - delay: u16, - transparent_color: u8, - terminator: u8, + block_size: u8, + flags: u8, + delay: u16, + transparent_color: u8, + terminator: u8, } impl GraphicControlExtension { - pub fn read(reader: &mut T) -> Result { - Ok(GraphicControlExtension { - block_size: reader.read_u8()?, - flags: reader.read_u8()?, - delay: reader.read_u16::()?, - transparent_color: reader.read_u8()?, - terminator: reader.read_u8()?, - }) - } + pub fn read(reader: &mut T) -> Result { + Ok(GraphicControlExtension { + block_size: reader.read_u8()?, + flags: reader.read_u8()?, + delay: reader.read_u16::()?, + transparent_color: reader.read_u8()?, + terminator: reader.read_u8()?, + }) + } - pub fn write(&self, writer: &mut T) -> Result<(), GifError> { - writer.write_u8(self.block_size)?; - writer.write_u8(self.flags)?; - writer.write_u16::(self.delay)?; - writer.write_u8(self.transparent_color)?; - writer.write_u8(self.terminator)?; - Ok(()) - } + pub fn write(&self, writer: &mut T) -> Result<(), GifError> { + writer.write_u8(self.block_size)?; + writer.write_u8(self.flags)?; + writer.write_u16::(self.delay)?; + writer.write_u8(self.transparent_color)?; + writer.write_u8(self.terminator)?; + Ok(()) + } } #[derive(Debug, Clone)] struct PlainTextExtension { - block_size: u8, - text_x: u16, - text_y: u16, - text_width: u16, - text_height: u16, - cell_width: u8, - cell_height: u8, - foreground_color: u8, - background_color: u8, - data: Box<[u8]>, + block_size: u8, + text_x: u16, + text_y: u16, + text_width: u16, + text_height: u16, + cell_width: u8, + cell_height: u8, + foreground_color: u8, + background_color: u8, + data: Box<[u8]>, } #[allow(dead_code)] impl PlainTextExtension { - pub fn read(reader: &mut T) -> Result { - Ok(PlainTextExtension { - block_size: reader.read_u8()?, - text_x: reader.read_u16::()?, - text_y: reader.read_u16::()?, - text_width: reader.read_u16::()?, - text_height: reader.read_u16::()?, - cell_width: reader.read_u8()?, - cell_height: reader.read_u8()?, - foreground_color: reader.read_u8()?, - background_color: reader.read_u8()?, - data: read_raw_sub_block_data(reader)?, - }) - } + pub fn read(reader: &mut T) -> Result { + Ok(PlainTextExtension { + block_size: reader.read_u8()?, + text_x: reader.read_u16::()?, + text_y: reader.read_u16::()?, + text_width: reader.read_u16::()?, + text_height: reader.read_u16::()?, + cell_width: reader.read_u8()?, + cell_height: reader.read_u8()?, + foreground_color: reader.read_u8()?, + background_color: reader.read_u8()?, + data: read_raw_sub_block_data(reader)?, + }) + } - pub fn write(&self, writer: &mut T) -> Result<(), GifError> { - writer.write_u8(self.block_size)?; - writer.write_u16::(self.text_x)?; - writer.write_u16::(self.text_y)?; - writer.write_u16::(self.text_width)?; - writer.write_u16::(self.text_height)?; - writer.write_u8(self.cell_width)?; - writer.write_u8(self.cell_height)?; - writer.write_u8(self.foreground_color)?; - writer.write_u8(self.background_color)?; - write_raw_sub_block_data(&self.data, writer)?; - Ok(()) - } + pub fn write(&self, writer: &mut T) -> Result<(), GifError> { + writer.write_u8(self.block_size)?; + writer.write_u16::(self.text_x)?; + writer.write_u16::(self.text_y)?; + writer.write_u16::(self.text_width)?; + writer.write_u16::(self.text_height)?; + writer.write_u8(self.cell_width)?; + writer.write_u8(self.cell_height)?; + writer.write_u8(self.foreground_color)?; + writer.write_u8(self.background_color)?; + write_raw_sub_block_data(&self.data, writer)?; + Ok(()) + } } #[derive(Debug, Clone)] struct ApplicationExtension { - block_size: u8, - identifier: [u8; 8], - authentication_code: [u8; 3], - data: Box<[u8]>, + block_size: u8, + identifier: [u8; 8], + authentication_code: [u8; 3], + data: Box<[u8]>, } #[allow(dead_code)] impl ApplicationExtension { - pub fn read(reader: &mut T) -> Result { - let block_size = reader.read_u8()?; - let mut identifier = [0u8; 8]; - reader.read_exact(&mut identifier)?; - let mut authentication_code = [0u8; 3]; - reader.read_exact(&mut authentication_code)?; - Ok(ApplicationExtension { - block_size, - identifier, - authentication_code, - data: read_raw_sub_block_data(reader)?, - }) - } + pub fn read(reader: &mut T) -> Result { + let block_size = reader.read_u8()?; + let mut identifier = [0u8; 8]; + reader.read_exact(&mut identifier)?; + let mut authentication_code = [0u8; 3]; + reader.read_exact(&mut authentication_code)?; + Ok(ApplicationExtension { + block_size, + identifier, + authentication_code, + data: read_raw_sub_block_data(reader)?, + }) + } - pub fn write(&self, writer: &mut T) -> Result<(), GifError> { - writer.write_u8(self.block_size)?; - writer.write_all(&self.identifier)?; - writer.write_all(&self.authentication_code)?; - write_raw_sub_block_data(&self.data, writer)?; - Ok(()) - } + pub fn write(&self, writer: &mut T) -> Result<(), GifError> { + writer.write_u8(self.block_size)?; + writer.write_all(&self.identifier)?; + writer.write_all(&self.authentication_code)?; + write_raw_sub_block_data(&self.data, writer)?; + Ok(()) + } } #[derive(Debug, Clone)] struct CommentExtension { - data: Box<[u8]>, + data: Box<[u8]>, } #[allow(dead_code)] impl CommentExtension { - pub fn read(reader: &mut T) -> Result { - Ok(CommentExtension { - data: read_raw_sub_block_data(reader)?, - }) - } + pub fn read(reader: &mut T) -> Result { + Ok(CommentExtension { + data: read_raw_sub_block_data(reader)?, + }) + } - pub fn write(&self, writer: &mut T) -> Result<(), GifError> { - write_raw_sub_block_data(&self.data, writer)?; - Ok(()) - } + pub fn write(&self, writer: &mut T) -> Result<(), GifError> { + write_raw_sub_block_data(&self.data, writer)?; + Ok(()) + } } #[derive(Debug, Clone)] struct LocalImageDescriptor { - x: u16, - y: u16, - width: u16, - height: u16, - flags: u8, + x: u16, + y: u16, + width: u16, + height: u16, + flags: u8, } #[allow(dead_code)] impl LocalImageDescriptor { - pub fn has_local_color_table(&self) -> bool { - self.flags & 0b10000000 != 0 - } + pub fn has_local_color_table(&self) -> bool { + self.flags & 0b10000000 != 0 + } - pub fn set_local_color_table(&mut self, value: bool) { - self.flags |= (value as u8).wrapping_shl(7); - } + pub fn set_local_color_table(&mut self, value: bool) { + self.flags |= (value as u8).wrapping_shl(7); + } - pub fn is_color_table_entries_sorted(&self) -> bool { - self.flags & 0b00100000 != 0 - } + pub fn is_color_table_entries_sorted(&self) -> bool { + self.flags & 0b00100000 != 0 + } - pub fn set_color_table_entries_sorted(&mut self, value: bool) { - self.flags |= (value as u8).wrapping_shl(5); - } + pub fn set_color_table_entries_sorted(&mut self, value: bool) { + self.flags |= (value as u8).wrapping_shl(5); + } - pub fn local_color_table_bits(&self) -> u8 { - self.flags & 0b00000111 - } + pub fn local_color_table_bits(&self) -> u8 { + self.flags & 0b00000111 + } - pub fn set_local_color_table_bits(&mut self, value: u8) { - self.flags |= value & 0b111; - } + pub fn set_local_color_table_bits(&mut self, value: u8) { + self.flags |= value & 0b111; + } - pub fn read(reader: &mut T) -> Result { - Ok(LocalImageDescriptor { - x: reader.read_u16::()?, - y: reader.read_u16::()?, - width: reader.read_u16::()?, - height: reader.read_u16::()?, - flags: reader.read_u8()? - }) - } + pub fn read(reader: &mut T) -> Result { + Ok(LocalImageDescriptor { + x: reader.read_u16::()?, + y: reader.read_u16::()?, + width: reader.read_u16::()?, + height: reader.read_u16::()?, + flags: reader.read_u8()?, + }) + } - pub fn write(&self, writer: &mut T) -> Result<(), GifError> { - writer.write_u16::(self.x)?; - writer.write_u16::(self.y)?; - writer.write_u16::(self.width)?; - writer.write_u16::(self.height)?; - writer.write_u8(self.flags)?; - Ok(()) - } + pub fn write(&self, writer: &mut T) -> Result<(), GifError> { + writer.write_u16::(self.x)?; + writer.write_u16::(self.y)?; + writer.write_u16::(self.width)?; + writer.write_u16::(self.height)?; + writer.write_u8(self.flags)?; + Ok(()) + } } fn load_image_section( - reader: &mut T, - gif_header: &GifHeader, - _graphic_control: &Option, + reader: &mut T, + gif_header: &GifHeader, + _graphic_control: &Option, ) -> Result<(Bitmap, Option), GifError> { - let descriptor = LocalImageDescriptor::read(reader)?; + let descriptor = LocalImageDescriptor::read(reader)?; - let palette: Option; - if descriptor.has_local_color_table() { - let num_colors = bits_to_num_colors(descriptor.local_color_table_bits() as u32) as usize; - palette = Some(Palette::load_num_colors_from_bytes( - reader, - PaletteFormat::Normal, - num_colors, - )?); - } else { - palette = None; // we expect that there was a global color table previously - } + let palette: Option; + if descriptor.has_local_color_table() { + let num_colors = bits_to_num_colors(descriptor.local_color_table_bits() as u32) as usize; + palette = Some(Palette::load_num_colors_from_bytes( + reader, + PaletteFormat::Normal, + num_colors, + )?); + } else { + palette = None; // we expect that there was a global color table previously + } - let mut bitmap = Bitmap::new(gif_header.screen_width as u32, gif_header.screen_height as u32).unwrap(); - let mut writer = bitmap.pixels_mut(); - lzw_decode(reader, &mut writer)?; + let mut bitmap = Bitmap::new(gif_header.screen_width as u32, gif_header.screen_height as u32).unwrap(); + let mut writer = bitmap.pixels_mut(); + lzw_decode(reader, &mut writer)?; - Ok((bitmap, palette)) + Ok((bitmap, palette)) } fn save_image_section( - writer: &mut T, - bitmap: &Bitmap, + writer: &mut T, + bitmap: &Bitmap, ) -> Result<(), GifError> { - writer.write_u8(IMAGE_DESCRIPTOR_SEPARATOR)?; - let image_descriptor = LocalImageDescriptor { - x: 0, - y: 0, - width: bitmap.width as u16, - height: bitmap.height as u16, - flags: 0, // again, we're not using local color tables, so no flags to set here - }; - image_descriptor.write(writer)?; + writer.write_u8(IMAGE_DESCRIPTOR_SEPARATOR)?; + let image_descriptor = LocalImageDescriptor { + x: 0, + y: 0, + width: bitmap.width as u16, + height: bitmap.height as u16, + flags: 0, // again, we're not using local color tables, so no flags to set here + }; + image_descriptor.write(writer)?; - // todo: allow this to changed based on the input palette, if/when we allow gifs to be - // saved with smaller than 256 colour palettes - let lzw_minimum_code_size = 8; + // todo: allow this to changed based on the input palette, if/when we allow gifs to be + // saved with smaller than 256 colour palettes + let lzw_minimum_code_size = 8; - let mut reader = bitmap.pixels(); - lzw_encode(&mut reader, writer, lzw_minimum_code_size as usize)?; + let mut reader = bitmap.pixels(); + lzw_encode(&mut reader, writer, lzw_minimum_code_size as usize)?; - Ok(()) + Ok(()) } impl Bitmap { - pub fn load_gif_bytes( - reader: &mut T, - ) -> Result<(Bitmap, Palette), GifError> { - let header = GifHeader::read(reader)?; - if header.signature != *b"GIF" || header.version != *b"89a" { - return Err(GifError::BadFile(String::from("Expected GIF89a header signature"))); - } + pub fn load_gif_bytes( + reader: &mut T, + ) -> Result<(Bitmap, Palette), GifError> { + let header = GifHeader::read(reader)?; + if header.signature != *b"GIF" || header.version != *b"89a" { + return Err(GifError::BadFile(String::from("Expected GIF89a header signature"))); + } - // note that we might later overwrite this with a local color table (if this gif has one) - let mut palette: Option; - if header.has_global_color_table() { - let num_colors = bits_to_num_colors(header.global_color_table_bits() as u32) as usize; - palette = Some(Palette::load_num_colors_from_bytes( - reader, - PaletteFormat::Normal, - num_colors, - )?); - } else { - palette = None; // we expect to find a local color table later - } + // note that we might later overwrite this with a local color table (if this gif has one) + let mut palette: Option; + if header.has_global_color_table() { + let num_colors = bits_to_num_colors(header.global_color_table_bits() as u32) as usize; + palette = Some(Palette::load_num_colors_from_bytes( + reader, + PaletteFormat::Normal, + num_colors, + )?); + } else { + palette = None; // we expect to find a local color table later + } - let mut bitmap: Option = None; - let mut current_graphic_control: Option = None; + let mut bitmap: Option = None; + let mut current_graphic_control: Option = None; - loop { - let current_byte = reader.read_u8()?; + loop { + let current_byte = reader.read_u8()?; - // check for eof via the gif's "trailer" block ... - if current_byte == 0x3b { - break; - } - // if we have already successfully read a bitmap and palette from this file, we can - // stop reading the rest. we only care about the first frame (if there are multiple) - // and palette we find - if bitmap.is_some() && palette.is_some() { - break; - } + // check for eof via the gif's "trailer" block ... + if current_byte == 0x3b { + break; + } + // if we have already successfully read a bitmap and palette from this file, we can + // stop reading the rest. we only care about the first frame (if there are multiple) + // and palette we find + if bitmap.is_some() && palette.is_some() { + break; + } - match current_byte { - GIF_TRAILER => break, - IMAGE_DESCRIPTOR_SEPARATOR => { - let (frame_bitmap, frame_palette) = load_image_section(reader, &header, ¤t_graphic_control)?; - bitmap = Some(frame_bitmap); - if frame_palette.is_some() { - palette = frame_palette; - } - }, - EXTENSION_INTRODUCER => { - let label = GifExtensionLabel::from(reader.read_u8()?)?; - match label { - GifExtensionLabel::GraphicControl => { - current_graphic_control = Some(GraphicControlExtension::read(reader)?); - }, - GifExtensionLabel::PlainText => { - let _plain_text = PlainTextExtension::read(reader)?; - // todo: do something with this maybe - }, - GifExtensionLabel::Application => { - let _application = ApplicationExtension::read(reader)?; - // todo: do something with this maybe - }, - GifExtensionLabel::Comment => { - let _comment = CommentExtension::read(reader)?; - // todo: do something with this maybe - }, - } - }, - _ => { - return Err(GifError::BadFile(format!("Unexpected byte found {} not a file trailer, image separator or extension introducer", current_byte))) - } - } - } + match current_byte { + GIF_TRAILER => break, + IMAGE_DESCRIPTOR_SEPARATOR => { + let (frame_bitmap, frame_palette) = load_image_section(reader, &header, ¤t_graphic_control)?; + bitmap = Some(frame_bitmap); + if frame_palette.is_some() { + palette = frame_palette; + } + } + EXTENSION_INTRODUCER => { + let label = GifExtensionLabel::from(reader.read_u8()?)?; + match label { + GifExtensionLabel::GraphicControl => { + current_graphic_control = Some(GraphicControlExtension::read(reader)?); + } + GifExtensionLabel::PlainText => { + let _plain_text = PlainTextExtension::read(reader)?; + // todo: do something with this maybe + } + GifExtensionLabel::Application => { + let _application = ApplicationExtension::read(reader)?; + // todo: do something with this maybe + } + GifExtensionLabel::Comment => { + let _comment = CommentExtension::read(reader)?; + // todo: do something with this maybe + } + } + } + _ => { + return Err(GifError::BadFile(format!("Unexpected byte found {} not a file trailer, image separator or extension introducer", current_byte))); + } + } + } - if bitmap.is_none() { - return Err(GifError::BadFile(String::from("No image data was found"))); - } - if palette.is_none() { - return Err(GifError::BadFile(String::from("No palette data was found"))); - } + if bitmap.is_none() { + return Err(GifError::BadFile(String::from("No image data was found"))); + } + if palette.is_none() { + return Err(GifError::BadFile(String::from("No palette data was found"))); + } - Ok((bitmap.unwrap(), palette.unwrap())) - } + Ok((bitmap.unwrap(), palette.unwrap())) + } - pub fn load_gif_file(path: &Path) -> Result<(Bitmap, Palette), GifError> { - let f = File::open(path)?; - let mut reader = BufReader::new(f); - Self::load_gif_bytes(&mut reader) - } + pub fn load_gif_file(path: &Path) -> Result<(Bitmap, Palette), GifError> { + let f = File::open(path)?; + let mut reader = BufReader::new(f); + Self::load_gif_bytes(&mut reader) + } - pub fn to_gif_bytes( - &self, - writer: &mut T, - palette: &Palette, - settings: GifSettings, - ) -> Result<(), GifError> { + pub fn to_gif_bytes( + &self, + writer: &mut T, + palette: &Palette, + settings: GifSettings, + ) -> Result<(), GifError> { + let mut header = GifHeader { + signature: *b"GIF", + version: *b"89a", + screen_width: self.width as u16, + screen_height: self.height as u16, + flags: 0, + background_color: 0, + aspect_ratio: 0, + }; + header.set_global_color_table(true); + header.set_global_color_table_bits(BITS_FOR_256_COLORS as u8); + header.set_color_resolution_bits(BITS_FOR_256_COLORS as u8); + header.write(writer)?; - let mut header = GifHeader { - signature: *b"GIF", - version: *b"89a", - screen_width: self.width as u16, - screen_height: self.height as u16, - flags: 0, - background_color: 0, - aspect_ratio: 0, - }; - header.set_global_color_table(true); - header.set_global_color_table_bits(BITS_FOR_256_COLORS as u8); - header.set_color_resolution_bits(BITS_FOR_256_COLORS as u8); - header.write(writer)?; + // write the provided palette out as the global color table. we will not be providing any + // local color tables. + palette.to_bytes(writer, PaletteFormat::Normal)?; - // write the provided palette out as the global color table. we will not be providing any - // local color tables. - palette.to_bytes(writer, PaletteFormat::Normal)?; + let transparent_color: u8; + match settings { + GifSettings::Default => { + transparent_color = 0; + } + GifSettings::TransparentColor(color) => { + transparent_color = color; + } + } - let transparent_color: u8; - match settings { - GifSettings::Default => { - transparent_color = 0; - }, - GifSettings::TransparentColor(color) => { - transparent_color = color; - } - } + writer.write_u8(EXTENSION_INTRODUCER)?; + writer.write_u8(GifExtensionLabel::GraphicControl as u8)?; + let graphic_control = GraphicControlExtension { + block_size: 4, + flags: 0, + delay: 0, + transparent_color, + terminator: 0, + }; + graphic_control.write(writer)?; - writer.write_u8(EXTENSION_INTRODUCER)?; - writer.write_u8(GifExtensionLabel::GraphicControl as u8)?; - let graphic_control = GraphicControlExtension { - block_size: 4, - flags: 0, - delay: 0, - transparent_color, - terminator: 0, - }; - graphic_control.write(writer)?; + save_image_section(writer, &self)?; - save_image_section(writer, &self)?; + writer.write_u8(GIF_TRAILER)?; + Ok(()) + } - writer.write_u8(GIF_TRAILER)?; - Ok(()) - } - - pub fn to_gif_file( - &self, - path: &Path, - palette: &Palette, - settings: GifSettings - ) -> Result<(), GifError> { - let f = File::create(path)?; - let mut writer = BufWriter::new(f); - self.to_gif_bytes(&mut writer, palette, settings) - } + pub fn to_gif_file( + &self, + path: &Path, + palette: &Palette, + settings: GifSettings, + ) -> Result<(), GifError> { + let f = File::create(path)?; + let mut writer = BufWriter::new(f); + self.to_gif_bytes(&mut writer, palette, settings) + } } #[cfg(test)] pub mod tests { - use tempfile::TempDir; + use tempfile::TempDir; - use super::*; + use super::*; - pub static TEST_BMP_PIXELS_RAW: &[u8] = include_bytes!("../../../test-assets/test_bmp_pixels_raw.bin"); - pub static TEST_LARGE_BMP_PIXELS_RAW: &[u8] = include_bytes!("../../../test-assets/test_large_bmp_pixels_raw.bin"); - pub static TEST_LARGE_BMP_PIXELS_RAW_2: &[u8] = include_bytes!("../../../test-assets/test_large_bmp_pixels_raw2.bin"); + pub static TEST_BMP_PIXELS_RAW: &[u8] = include_bytes!("../../../test-assets/test_bmp_pixels_raw.bin"); + pub static TEST_LARGE_BMP_PIXELS_RAW: &[u8] = include_bytes!("../../../test-assets/test_large_bmp_pixels_raw.bin"); + pub static TEST_LARGE_BMP_PIXELS_RAW_2: &[u8] = include_bytes!("../../../test-assets/test_large_bmp_pixels_raw2.bin"); - #[test] - fn load_and_save() -> Result<(), GifError> { - let dp2_palette = - Palette::load_from_file(Path::new("./test-assets/dp2.pal"), PaletteFormat::Normal) - .unwrap(); - let tmp_dir = TempDir::new()?; + #[test] + fn load_and_save() -> Result<(), GifError> { + let dp2_palette = + Palette::load_from_file(Path::new("./test-assets/dp2.pal"), PaletteFormat::Normal) + .unwrap(); + let tmp_dir = TempDir::new()?; - let (bmp, palette) = Bitmap::load_gif_file(Path::new("./test-assets/test.gif"))?; - assert_eq!(16, bmp.width()); - assert_eq!(16, bmp.height()); - assert_eq!(bmp.pixels(), TEST_BMP_PIXELS_RAW); - assert_eq!(palette, dp2_palette); + let (bmp, palette) = Bitmap::load_gif_file(Path::new("./test-assets/test.gif"))?; + assert_eq!(16, bmp.width()); + assert_eq!(16, bmp.height()); + assert_eq!(bmp.pixels(), TEST_BMP_PIXELS_RAW); + assert_eq!(palette, dp2_palette); - let save_path = tmp_dir.path().join("test_save.gif"); - bmp.to_gif_file(&save_path, &palette, GifSettings::Default)?; - let (reloaded_bmp, reloaded_palette) = Bitmap::load_gif_file(&save_path)?; - assert_eq!(16, reloaded_bmp.width()); - assert_eq!(16, reloaded_bmp.height()); - assert_eq!(reloaded_bmp.pixels(), TEST_BMP_PIXELS_RAW); - assert_eq!(reloaded_palette, dp2_palette); + let save_path = tmp_dir.path().join("test_save.gif"); + bmp.to_gif_file(&save_path, &palette, GifSettings::Default)?; + let (reloaded_bmp, reloaded_palette) = Bitmap::load_gif_file(&save_path)?; + assert_eq!(16, reloaded_bmp.width()); + assert_eq!(16, reloaded_bmp.height()); + assert_eq!(reloaded_bmp.pixels(), TEST_BMP_PIXELS_RAW); + assert_eq!(reloaded_palette, dp2_palette); - Ok(()) - } + Ok(()) + } - #[test] - fn load_and_save_larger_image() -> Result<(), GifError> { - // this test is mostly useful to get a LZW decode and encode that includes at least one - // "clear code" and accompanying table reset + #[test] + fn load_and_save_larger_image() -> Result<(), GifError> { + // this test is mostly useful to get a LZW decode and encode that includes at least one + // "clear code" and accompanying table reset - let tmp_dir = TempDir::new()?; + let tmp_dir = TempDir::new()?; - // first image + // first image - let (bmp, palette) = Bitmap::load_gif_file(Path::new("./test-assets/test_image.gif"))?; - assert_eq!(320, bmp.width()); - assert_eq!(200, bmp.height()); - assert_eq!(bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW); + let (bmp, palette) = Bitmap::load_gif_file(Path::new("./test-assets/test_image.gif"))?; + assert_eq!(320, bmp.width()); + assert_eq!(200, bmp.height()); + assert_eq!(bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW); - let save_path = tmp_dir.path().join("test_save.gif"); - bmp.to_gif_file(&save_path, &palette, GifSettings::Default)?; - let (reloaded_bmp, _) = Bitmap::load_gif_file(&save_path)?; - assert_eq!(320, reloaded_bmp.width()); - assert_eq!(200, reloaded_bmp.height()); - assert_eq!(reloaded_bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW); + let save_path = tmp_dir.path().join("test_save.gif"); + bmp.to_gif_file(&save_path, &palette, GifSettings::Default)?; + let (reloaded_bmp, _) = Bitmap::load_gif_file(&save_path)?; + assert_eq!(320, reloaded_bmp.width()); + assert_eq!(200, reloaded_bmp.height()); + assert_eq!(reloaded_bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW); - // second image + // second image - let (bmp, palette) = Bitmap::load_gif_file(Path::new("./test-assets/test_image2.gif"))?; - assert_eq!(320, bmp.width()); - assert_eq!(200, bmp.height()); - assert_eq!(bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW_2); + let (bmp, palette) = Bitmap::load_gif_file(Path::new("./test-assets/test_image2.gif"))?; + assert_eq!(320, bmp.width()); + assert_eq!(200, bmp.height()); + assert_eq!(bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW_2); - let save_path = tmp_dir.path().join("test_save_2.gif"); - bmp.to_gif_file(&save_path, &palette, GifSettings::Default)?; - let (reloaded_bmp, _) = Bitmap::load_gif_file(&save_path)?; - assert_eq!(320, reloaded_bmp.width()); - assert_eq!(200, reloaded_bmp.height()); - assert_eq!(reloaded_bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW_2); + let save_path = tmp_dir.path().join("test_save_2.gif"); + bmp.to_gif_file(&save_path, &palette, GifSettings::Default)?; + let (reloaded_bmp, _) = Bitmap::load_gif_file(&save_path)?; + assert_eq!(320, reloaded_bmp.width()); + assert_eq!(200, reloaded_bmp.height()); + assert_eq!(reloaded_bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW_2); - Ok(()) - } + Ok(()) + } } diff --git a/libretrogd/src/graphics/bitmap/iff.rs b/libretrogd/src/graphics/bitmap/iff.rs index a97d64b..7f91f37 100644 --- a/libretrogd/src/graphics/bitmap/iff.rs +++ b/libretrogd/src/graphics/bitmap/iff.rs @@ -11,665 +11,665 @@ use crate::utils::packbits::*; #[derive(Error, Debug)] pub enum IffError { - #[error("Bad or unsupported IFF file: {0}")] - BadFile(String), + #[error("Bad or unsupported IFF file: {0}")] + BadFile(String), - #[error("IFF palette data error")] - BadPalette(#[from] PaletteError), + #[error("IFF palette data error")] + BadPalette(#[from] PaletteError), - #[error("PackBits error")] - PackBitsError(#[from] PackBitsError), + #[error("PackBits error")] + PackBitsError(#[from] PackBitsError), - #[error("IFF I/O error")] - IOError(#[from] std::io::Error), + #[error("IFF I/O error")] + IOError(#[from] std::io::Error), } pub enum IffFormat { - Pbm, - PbmUncompressed, - Ilbm, - IlbmUncompressed, + Pbm, + PbmUncompressed, + Ilbm, + IlbmUncompressed, } impl IffFormat { - pub fn compressed(&self) -> bool { - use IffFormat::*; - match self { - Pbm | Ilbm => true, - PbmUncompressed | IlbmUncompressed => false, - } - } + pub fn compressed(&self) -> bool { + use IffFormat::*; + match self { + Pbm | Ilbm => true, + PbmUncompressed | IlbmUncompressed => false, + } + } - pub fn chunky(&self) -> bool { - use IffFormat::*; - match self { - Pbm | PbmUncompressed => true, - Ilbm | IlbmUncompressed => false, - } - } + pub fn chunky(&self) -> bool { + use IffFormat::*; + match self { + Pbm | PbmUncompressed => true, + Ilbm | IlbmUncompressed => false, + } + } - pub fn type_id(&self) -> [u8; 4] { - use IffFormat::*; - match self { - Pbm | PbmUncompressed => *b"PBM ", - Ilbm | IlbmUncompressed => *b"ILBM", - } - } + pub fn type_id(&self) -> [u8; 4] { + use IffFormat::*; + match self { + Pbm | PbmUncompressed => *b"PBM ", + Ilbm | IlbmUncompressed => *b"ILBM", + } + } } #[derive(Debug, Copy, Clone)] #[repr(packed)] struct IffId { - id: [u8; 4], + id: [u8; 4], } impl IffId { - pub fn read(reader: &mut T) -> Result { - let mut id = [0u8; 4]; - reader.read_exact(&mut id)?; - Ok(IffId { id }) - } + pub fn read(reader: &mut T) -> Result { + let mut id = [0u8; 4]; + reader.read_exact(&mut id)?; + Ok(IffId { id }) + } - pub fn write(&self, writer: &mut T) -> Result<(), IffError> { - writer.write_all(&self.id)?; - Ok(()) - } + pub fn write(&self, writer: &mut T) -> Result<(), IffError> { + writer.write_all(&self.id)?; + Ok(()) + } } #[derive(Debug, Copy, Clone)] #[repr(packed)] struct FormChunkHeader { - chunk_id: IffId, - size: u32, - type_id: IffId, + chunk_id: IffId, + size: u32, + type_id: IffId, } impl FormChunkHeader { - pub fn read(reader: &mut T) -> Result { - let chunk_id = IffId::read(reader)?; - let size = reader.read_u32::()?; - let type_id = IffId::read(reader)?; - Ok(FormChunkHeader { - chunk_id, - size, - type_id, - }) - } + pub fn read(reader: &mut T) -> Result { + let chunk_id = IffId::read(reader)?; + let size = reader.read_u32::()?; + let type_id = IffId::read(reader)?; + Ok(FormChunkHeader { + chunk_id, + size, + type_id, + }) + } - pub fn write(&self, writer: &mut T) -> Result<(), IffError> { - self.chunk_id.write(writer)?; - writer.write_u32::(self.size)?; - self.type_id.write(writer)?; - Ok(()) - } + pub fn write(&self, writer: &mut T) -> Result<(), IffError> { + self.chunk_id.write(writer)?; + writer.write_u32::(self.size)?; + self.type_id.write(writer)?; + Ok(()) + } } #[derive(Debug, Copy, Clone)] #[repr(packed)] struct SubChunkHeader { - chunk_id: IffId, - size: u32, + chunk_id: IffId, + size: u32, } impl SubChunkHeader { - pub fn read(reader: &mut T) -> Result { - let chunk_id = IffId::read(reader)?; - let mut size = reader.read_u32::()?; - if (size & 1) == 1 { - size += 1; // account for the padding byte - } - Ok(SubChunkHeader { chunk_id, size }) - } + pub fn read(reader: &mut T) -> Result { + let chunk_id = IffId::read(reader)?; + let mut size = reader.read_u32::()?; + if (size & 1) == 1 { + size += 1; // account for the padding byte + } + Ok(SubChunkHeader { chunk_id, size }) + } - pub fn write(&self, writer: &mut T) -> Result<(), IffError> { - self.chunk_id.write(writer)?; - writer.write_u32::(self.size)?; - Ok(()) - } + pub fn write(&self, writer: &mut T) -> Result<(), IffError> { + self.chunk_id.write(writer)?; + writer.write_u32::(self.size)?; + Ok(()) + } } #[derive(Debug, Copy, Clone)] #[repr(packed)] struct BMHDChunk { - width: u16, - height: u16, - left: u16, - top: u16, - bitplanes: u8, - masking: u8, - compress: u8, - padding: u8, - transparency: u16, - x_aspect_ratio: u8, - y_aspect_ratio: u8, - page_width: u16, - page_height: u16, + width: u16, + height: u16, + left: u16, + top: u16, + bitplanes: u8, + masking: u8, + compress: u8, + padding: u8, + transparency: u16, + x_aspect_ratio: u8, + y_aspect_ratio: u8, + page_width: u16, + page_height: u16, } impl BMHDChunk { - pub fn read(reader: &mut T) -> Result { - Ok(BMHDChunk { - width: reader.read_u16::()?, - height: reader.read_u16::()?, - left: reader.read_u16::()?, - top: reader.read_u16::()?, - bitplanes: reader.read_u8()?, - masking: reader.read_u8()?, - compress: reader.read_u8()?, - padding: reader.read_u8()?, - transparency: reader.read_u16::()?, - x_aspect_ratio: reader.read_u8()?, - y_aspect_ratio: reader.read_u8()?, - page_width: reader.read_u16::()?, - page_height: reader.read_u16::()?, - }) - } + pub fn read(reader: &mut T) -> Result { + Ok(BMHDChunk { + width: reader.read_u16::()?, + height: reader.read_u16::()?, + left: reader.read_u16::()?, + top: reader.read_u16::()?, + bitplanes: reader.read_u8()?, + masking: reader.read_u8()?, + compress: reader.read_u8()?, + padding: reader.read_u8()?, + transparency: reader.read_u16::()?, + x_aspect_ratio: reader.read_u8()?, + y_aspect_ratio: reader.read_u8()?, + page_width: reader.read_u16::()?, + page_height: reader.read_u16::()?, + }) + } - pub fn write(&self, writer: &mut T) -> Result<(), IffError> { - writer.write_u16::(self.width)?; - writer.write_u16::(self.height)?; - writer.write_u16::(self.left)?; - writer.write_u16::(self.top)?; - writer.write_u8(self.bitplanes)?; - writer.write_u8(self.masking)?; - writer.write_u8(self.compress)?; - writer.write_u8(self.padding)?; - writer.write_u16::(self.transparency)?; - writer.write_u8(self.x_aspect_ratio)?; - writer.write_u8(self.y_aspect_ratio)?; - writer.write_u16::(self.page_width)?; - writer.write_u16::(self.page_height)?; - Ok(()) - } + pub fn write(&self, writer: &mut T) -> Result<(), IffError> { + writer.write_u16::(self.width)?; + writer.write_u16::(self.height)?; + writer.write_u16::(self.left)?; + writer.write_u16::(self.top)?; + writer.write_u8(self.bitplanes)?; + writer.write_u8(self.masking)?; + writer.write_u8(self.compress)?; + writer.write_u8(self.padding)?; + writer.write_u16::(self.transparency)?; + writer.write_u8(self.x_aspect_ratio)?; + writer.write_u8(self.y_aspect_ratio)?; + writer.write_u16::(self.page_width)?; + writer.write_u16::(self.page_height)?; + Ok(()) + } } fn merge_bitplane(plane: u32, src: &[u8], dest: &mut [u8], row_size: usize) { - let bitmask = 1 << plane; - for x in 0..row_size { - let data = src[x]; - if (data & 128) > 0 { - dest[x * 8] |= bitmask; - } - if (data & 64) > 0 { - dest[(x * 8) + 1] |= bitmask; - } - if (data & 32) > 0 { - dest[(x * 8) + 2] |= bitmask; - } - if (data & 16) > 0 { - dest[(x * 8) + 3] |= bitmask; - } - if (data & 8) > 0 { - dest[(x * 8) + 4] |= bitmask; - } - if (data & 4) > 0 { - dest[(x * 8) + 5] |= bitmask; - } - if (data & 2) > 0 { - dest[(x * 8) + 6] |= bitmask; - } - if (data & 1) > 0 { - dest[(x * 8) + 7] |= bitmask; - } - } + let bitmask = 1 << plane; + for x in 0..row_size { + let data = src[x]; + if (data & 128) > 0 { + dest[x * 8] |= bitmask; + } + if (data & 64) > 0 { + dest[(x * 8) + 1] |= bitmask; + } + if (data & 32) > 0 { + dest[(x * 8) + 2] |= bitmask; + } + if (data & 16) > 0 { + dest[(x * 8) + 3] |= bitmask; + } + if (data & 8) > 0 { + dest[(x * 8) + 4] |= bitmask; + } + if (data & 4) > 0 { + dest[(x * 8) + 5] |= bitmask; + } + if (data & 2) > 0 { + dest[(x * 8) + 6] |= bitmask; + } + if (data & 1) > 0 { + dest[(x * 8) + 7] |= bitmask; + } + } } fn extract_bitplane(plane: u32, src: &[u8], dest: &mut [u8], row_size: usize) { - let bitmask = 1 << plane; - let mut src_base_index = 0; - for x in 0..row_size { - let mut data = 0; - if src[src_base_index] & bitmask != 0 { - data |= 128; - } - if src[src_base_index + 1] & bitmask != 0 { - data |= 64; - } - if src[src_base_index + 2] & bitmask != 0 { - data |= 32; - } - if src[src_base_index + 3] & bitmask != 0 { - data |= 16; - } - if src[src_base_index + 4] & bitmask != 0 { - data |= 8; - } - if src[src_base_index + 5] & bitmask != 0 { - data |= 4; - } - if src[src_base_index + 6] & bitmask != 0 { - data |= 2; - } - if src[src_base_index + 7] & bitmask != 0 { - data |= 1; - } + let bitmask = 1 << plane; + let mut src_base_index = 0; + for x in 0..row_size { + let mut data = 0; + if src[src_base_index] & bitmask != 0 { + data |= 128; + } + if src[src_base_index + 1] & bitmask != 0 { + data |= 64; + } + if src[src_base_index + 2] & bitmask != 0 { + data |= 32; + } + if src[src_base_index + 3] & bitmask != 0 { + data |= 16; + } + if src[src_base_index + 4] & bitmask != 0 { + data |= 8; + } + if src[src_base_index + 5] & bitmask != 0 { + data |= 4; + } + if src[src_base_index + 6] & bitmask != 0 { + data |= 2; + } + if src[src_base_index + 7] & bitmask != 0 { + data |= 1; + } - src_base_index += 8; - dest[x] = data; - } + src_base_index += 8; + dest[x] = data; + } } fn load_planar_body(reader: &mut T, bmhd: &BMHDChunk) -> Result { - let mut bitmap = Bitmap::new(bmhd.width as u32, bmhd.height as u32).unwrap(); + let mut bitmap = Bitmap::new(bmhd.width as u32, bmhd.height as u32).unwrap(); - let row_bytes = (((bmhd.width + 15) >> 4) << 1) as usize; - let mut buffer = vec![0u8; row_bytes]; + let row_bytes = (((bmhd.width + 15) >> 4) << 1) as usize; + let mut buffer = vec![0u8; row_bytes]; - for y in 0..bmhd.height { - // planar data is stored for each bitplane in sequence for the scanline. - // that is, ALL of bitplane1, followed by ALL of bitplane2, etc, NOT - // alternating after each pixel. if compression is enabled, it does NOT - // cross bitplane boundaries. each bitplane is compressed individually. - // bitplanes also do NOT cross the scanline boundary. basically, each - // scanline of pixel data, and within that, each of the bitplanes of - // pixel data found in each scanline can all be treated as they are all - // their own self-contained bit of data as far as this loading process - // is concerned (well, except that we merge all of the scanline's - // bitplanes together at the end of each line) + for y in 0..bmhd.height { + // planar data is stored for each bitplane in sequence for the scanline. + // that is, ALL of bitplane1, followed by ALL of bitplane2, etc, NOT + // alternating after each pixel. if compression is enabled, it does NOT + // cross bitplane boundaries. each bitplane is compressed individually. + // bitplanes also do NOT cross the scanline boundary. basically, each + // scanline of pixel data, and within that, each of the bitplanes of + // pixel data found in each scanline can all be treated as they are all + // their own self-contained bit of data as far as this loading process + // is concerned (well, except that we merge all of the scanline's + // bitplanes together at the end of each line) - // read all the bitplane rows per scanline - for plane in 0..(bmhd.bitplanes as u32) { - if bmhd.compress == 1 { - // decompress packed line for this bitplane only - buffer.clear(); - unpack_bits(reader, &mut buffer, row_bytes)? - } else { - // TODO: check this. maybe row_bytes calculation is wrong? either way, i don't - // think that DP2 or Grafx2 ever output uncompressed interleaved files ... - // just read all this bitplane's line data in as-is - reader.read_exact(&mut buffer)?; - } + // read all the bitplane rows per scanline + for plane in 0..(bmhd.bitplanes as u32) { + if bmhd.compress == 1 { + // decompress packed line for this bitplane only + buffer.clear(); + unpack_bits(reader, &mut buffer, row_bytes)? + } else { + // TODO: check this. maybe row_bytes calculation is wrong? either way, i don't + // think that DP2 or Grafx2 ever output uncompressed interleaved files ... + // just read all this bitplane's line data in as-is + reader.read_exact(&mut buffer)?; + } - // merge this bitplane data into the final destination. after all of - // the bitplanes have been loaded and merged in this way for this - // scanline, the destination pointer will contain VGA-friendly - // "chunky pixel"-format pixel data - merge_bitplane( - plane, - &buffer, - bitmap.pixels_at_mut(0, y as i32).unwrap(), - row_bytes, - ); - } - } + // merge this bitplane data into the final destination. after all of + // the bitplanes have been loaded and merged in this way for this + // scanline, the destination pointer will contain VGA-friendly + // "chunky pixel"-format pixel data + merge_bitplane( + plane, + &buffer, + bitmap.pixels_at_mut(0, y as i32).unwrap(), + row_bytes, + ); + } + } - Ok(bitmap) + Ok(bitmap) } fn load_chunky_body(reader: &mut T, bmhd: &BMHDChunk) -> Result { - let mut bitmap = Bitmap::new(bmhd.width as u32, bmhd.height as u32).unwrap(); + let mut bitmap = Bitmap::new(bmhd.width as u32, bmhd.height as u32).unwrap(); - for y in 0..bmhd.height { - if bmhd.compress == 1 { - // for compression-enabled, read row of pixels using PackBits - let mut writer = bitmap.pixels_at_mut(0, y as i32).unwrap(); - unpack_bits(reader, &mut writer, bmhd.width as usize)? - } else { - // for uncompressed, read row of pixels literally - let dest = &mut bitmap.pixels_at_mut(0, y as i32).unwrap()[0..bmhd.width as usize]; - reader.read_exact(dest)?; - } - } + for y in 0..bmhd.height { + if bmhd.compress == 1 { + // for compression-enabled, read row of pixels using PackBits + let mut writer = bitmap.pixels_at_mut(0, y as i32).unwrap(); + unpack_bits(reader, &mut writer, bmhd.width as usize)? + } else { + // for uncompressed, read row of pixels literally + let dest = &mut bitmap.pixels_at_mut(0, y as i32).unwrap()[0..bmhd.width as usize]; + reader.read_exact(dest)?; + } + } - Ok(bitmap) + Ok(bitmap) } fn write_planar_body( - writer: &mut T, - bitmap: &Bitmap, - bmhd: &BMHDChunk, + writer: &mut T, + bitmap: &Bitmap, + bmhd: &BMHDChunk, ) -> Result<(), IffError> { - let row_bytes = (((bitmap.width() + 15) >> 4) << 1) as usize; - let mut buffer = vec![0u8; row_bytes]; + let row_bytes = (((bitmap.width() + 15) >> 4) << 1) as usize; + let mut buffer = vec![0u8; row_bytes]; - for y in 0..bitmap.height() { - for plane in 0..(bmhd.bitplanes as u32) { - extract_bitplane( - plane, - bitmap.pixels_at(0, y as i32).unwrap(), - &mut buffer, - row_bytes, - ); + for y in 0..bitmap.height() { + for plane in 0..(bmhd.bitplanes as u32) { + extract_bitplane( + plane, + bitmap.pixels_at(0, y as i32).unwrap(), + &mut buffer, + row_bytes, + ); - if bmhd.compress == 1 { - // for compression-enabled, write this plane's pixels using PackBits - pack_bits(&mut buffer.as_slice(), writer, row_bytes)?; - } else { - // TODO: check this. maybe row_bytes calculation is wrong? either way, i don't - // think that DP2 or Grafx2 ever output uncompressed interleaved files ... - // for uncompressed, write this plane's pixels literally - writer.write_all(&buffer)?; - } - } - } + if bmhd.compress == 1 { + // for compression-enabled, write this plane's pixels using PackBits + pack_bits(&mut buffer.as_slice(), writer, row_bytes)?; + } else { + // TODO: check this. maybe row_bytes calculation is wrong? either way, i don't + // think that DP2 or Grafx2 ever output uncompressed interleaved files ... + // for uncompressed, write this plane's pixels literally + writer.write_all(&buffer)?; + } + } + } - Ok(()) + Ok(()) } fn write_chunky_body( - writer: &mut T, - bitmap: &Bitmap, - bmhd: &BMHDChunk, + writer: &mut T, + bitmap: &Bitmap, + bmhd: &BMHDChunk, ) -> Result<(), IffError> { - for y in 0..bitmap.height() { - if bmhd.compress == 1 { - // for compression-enabled, write row of pixels using PackBits - let mut reader = bitmap.pixels_at(0, y as i32).unwrap(); - pack_bits(&mut reader, writer, bitmap.width() as usize)?; - } else { - // for uncompressed, write out the row of pixels literally - let src = &bitmap.pixels_at(0, y as i32).unwrap()[0..bitmap.width() as usize]; - writer.write_all(src)?; - } - } + for y in 0..bitmap.height() { + if bmhd.compress == 1 { + // for compression-enabled, write row of pixels using PackBits + let mut reader = bitmap.pixels_at(0, y as i32).unwrap(); + pack_bits(&mut reader, writer, bitmap.width() as usize)?; + } else { + // for uncompressed, write out the row of pixels literally + let src = &bitmap.pixels_at(0, y as i32).unwrap()[0..bitmap.width() as usize]; + writer.write_all(src)?; + } + } - Ok(()) + Ok(()) } impl Bitmap { - pub fn load_iff_bytes( - reader: &mut T, - ) -> Result<(Bitmap, Palette), IffError> { - let form_chunk = FormChunkHeader::read(reader)?; - if form_chunk.chunk_id.id != *b"FORM" { - return Err(IffError::BadFile(String::from( - "Unexpected form chunk ID, probably not an IFF file", - ))); - } - if form_chunk.type_id.id != *b"ILBM" && form_chunk.type_id.id != *b"PBM " { - return Err(IffError::BadFile(String::from( - "Only ILBM or PBM formats are supported", - ))); - } + pub fn load_iff_bytes( + reader: &mut T, + ) -> Result<(Bitmap, Palette), IffError> { + let form_chunk = FormChunkHeader::read(reader)?; + if form_chunk.chunk_id.id != *b"FORM" { + return Err(IffError::BadFile(String::from( + "Unexpected form chunk ID, probably not an IFF file", + ))); + } + if form_chunk.type_id.id != *b"ILBM" && form_chunk.type_id.id != *b"PBM " { + return Err(IffError::BadFile(String::from( + "Only ILBM or PBM formats are supported", + ))); + } - let mut bmhd: Option = None; - let mut palette: Option = None; - let mut bitmap: Option = None; + let mut bmhd: Option = None; + let mut palette: Option = None; + let mut bitmap: Option = None; - loop { - let header = match SubChunkHeader::read(reader) { - Ok(header) => header, - Err(IffError::IOError(io_error)) - if io_error.kind() == io::ErrorKind::UnexpectedEof => - { - break - } - Err(err) => return Err(err), - }; - let chunk_data_position = reader.stream_position()?; + loop { + let header = match SubChunkHeader::read(reader) { + Ok(header) => header, + Err(IffError::IOError(io_error)) + if io_error.kind() == io::ErrorKind::UnexpectedEof => + { + break; + } + Err(err) => return Err(err), + }; + let chunk_data_position = reader.stream_position()?; - // todo: process chunk here - if header.chunk_id.id == *b"BMHD" { - bmhd = Some(BMHDChunk::read(reader)?); - if bmhd.as_ref().unwrap().bitplanes != 8 { - return Err(IffError::BadFile(String::from( - "Only 8bpp files are supported", - ))); - } - if bmhd.as_ref().unwrap().masking == 1 { - return Err(IffError::BadFile(String::from("Masking is not supported"))); - } - } else if header.chunk_id.id == *b"CMAP" { - if header.size != 768 { - return Err(IffError::BadFile(String::from( - "Only 256 color files are supported", - ))); - } - palette = Some(Palette::load_from_bytes(reader, PaletteFormat::Normal)?) - } else if header.chunk_id.id == *b"BODY" { - if let Some(bmhd) = &bmhd { - if form_chunk.type_id.id == *b"PBM " { - bitmap = Some(load_chunky_body(reader, bmhd)?); - } else { - bitmap = Some(load_planar_body(reader, bmhd)?); - } - } else { - // TODO: does this ever occur in practice? and if so, we can probably make some - // changes to allow for it ... - return Err(IffError::BadFile(String::from( - "BODY chunk occurs before BMHD chunk, or no BMHD chunk exists", - ))); - } - } + // todo: process chunk here + if header.chunk_id.id == *b"BMHD" { + bmhd = Some(BMHDChunk::read(reader)?); + if bmhd.as_ref().unwrap().bitplanes != 8 { + return Err(IffError::BadFile(String::from( + "Only 8bpp files are supported", + ))); + } + if bmhd.as_ref().unwrap().masking == 1 { + return Err(IffError::BadFile(String::from("Masking is not supported"))); + } + } else if header.chunk_id.id == *b"CMAP" { + if header.size != 768 { + return Err(IffError::BadFile(String::from( + "Only 256 color files are supported", + ))); + } + palette = Some(Palette::load_from_bytes(reader, PaletteFormat::Normal)?) + } else if header.chunk_id.id == *b"BODY" { + if let Some(bmhd) = &bmhd { + if form_chunk.type_id.id == *b"PBM " { + bitmap = Some(load_chunky_body(reader, bmhd)?); + } else { + bitmap = Some(load_planar_body(reader, bmhd)?); + } + } else { + // TODO: does this ever occur in practice? and if so, we can probably make some + // changes to allow for it ... + return Err(IffError::BadFile(String::from( + "BODY chunk occurs before BMHD chunk, or no BMHD chunk exists", + ))); + } + } - reader.seek(SeekFrom::Start(chunk_data_position + header.size as u64))?; - } + reader.seek(SeekFrom::Start(chunk_data_position + header.size as u64))?; + } - if bitmap.is_none() { - return Err(IffError::BadFile(String::from("No BODY chunk was found"))); - } - // TODO: we can probably make this optional ... - if palette.is_none() { - return Err(IffError::BadFile(String::from("No CMAP chunk was found"))); - } + if bitmap.is_none() { + return Err(IffError::BadFile(String::from("No BODY chunk was found"))); + } + // TODO: we can probably make this optional ... + if palette.is_none() { + return Err(IffError::BadFile(String::from("No CMAP chunk was found"))); + } - Ok((bitmap.unwrap(), palette.unwrap())) - } + Ok((bitmap.unwrap(), palette.unwrap())) + } - pub fn load_iff_file(path: &Path) -> Result<(Bitmap, Palette), IffError> { - let f = File::open(path)?; - let mut reader = BufReader::new(f); - Self::load_iff_bytes(&mut reader) - } + pub fn load_iff_file(path: &Path) -> Result<(Bitmap, Palette), IffError> { + let f = File::open(path)?; + let mut reader = BufReader::new(f); + Self::load_iff_bytes(&mut reader) + } - pub fn to_iff_bytes( - &self, - writer: &mut T, - palette: &Palette, - format: IffFormat, - ) -> Result<(), IffError> { - let form_chunk_position = writer.stream_position()?; + pub fn to_iff_bytes( + &self, + writer: &mut T, + palette: &Palette, + format: IffFormat, + ) -> Result<(), IffError> { + let form_chunk_position = writer.stream_position()?; - let mut form_chunk = FormChunkHeader { - chunk_id: IffId { id: *b"FORM" }, - type_id: IffId { - id: format.type_id(), - }, - size: 0, // filled in later once we know the size - }; + let mut form_chunk = FormChunkHeader { + chunk_id: IffId { id: *b"FORM" }, + type_id: IffId { + id: format.type_id(), + }, + size: 0, // filled in later once we know the size + }; - // skip over the form chunk for now. will come back here and write it out later once we - // know what the final size is - writer.seek(SeekFrom::Current( - std::mem::size_of::() as i64 - ))?; + // skip over the form chunk for now. will come back here and write it out later once we + // know what the final size is + writer.seek(SeekFrom::Current( + std::mem::size_of::() as i64 + ))?; - let bmhd_chunk_header = SubChunkHeader { - chunk_id: IffId { id: *b"BMHD" }, - size: std::mem::size_of::() as u32, - }; - let bmhd = BMHDChunk { - width: self.width() as u16, - height: self.height() as u16, - left: 0, - top: 0, - bitplanes: 8, - masking: 0, - compress: if format.compressed() { 1 } else { 0 }, - padding: 0, - transparency: 0, - // the following values are based on what DP2 writes out in 320x200 modes. good enough. - x_aspect_ratio: 5, - y_aspect_ratio: 6, - page_width: 320, - page_height: 200, - }; - bmhd_chunk_header.write(writer)?; - bmhd.write(writer)?; + let bmhd_chunk_header = SubChunkHeader { + chunk_id: IffId { id: *b"BMHD" }, + size: std::mem::size_of::() as u32, + }; + let bmhd = BMHDChunk { + width: self.width() as u16, + height: self.height() as u16, + left: 0, + top: 0, + bitplanes: 8, + masking: 0, + compress: if format.compressed() { 1 } else { 0 }, + padding: 0, + transparency: 0, + // the following values are based on what DP2 writes out in 320x200 modes. good enough. + x_aspect_ratio: 5, + y_aspect_ratio: 6, + page_width: 320, + page_height: 200, + }; + bmhd_chunk_header.write(writer)?; + bmhd.write(writer)?; - let cmap_chunk_header = SubChunkHeader { - chunk_id: IffId { id: *b"CMAP" }, - size: 768, - }; - cmap_chunk_header.write(writer)?; - palette.to_bytes(writer, PaletteFormat::Normal)?; + let cmap_chunk_header = SubChunkHeader { + chunk_id: IffId { id: *b"CMAP" }, + size: 768, + }; + cmap_chunk_header.write(writer)?; + palette.to_bytes(writer, PaletteFormat::Normal)?; - let body_position = writer.stream_position()?; + let body_position = writer.stream_position()?; - let mut body_chunk_header = SubChunkHeader { - chunk_id: IffId { id: *b"BODY" }, - size: 0, // filled in later once we know the size - }; + let mut body_chunk_header = SubChunkHeader { + chunk_id: IffId { id: *b"BODY" }, + size: 0, // filled in later once we know the size + }; - // skip over the body chunk header for now. we will again come back here and write it out - // later once we know what the final size again. - writer.seek(SeekFrom::Current( - std::mem::size_of::() as i64 - ))?; + // skip over the body chunk header for now. we will again come back here and write it out + // later once we know what the final size again. + writer.seek(SeekFrom::Current( + std::mem::size_of::() as i64 + ))?; - if format.chunky() { - write_chunky_body(writer, self, &bmhd)?; - } else { - write_planar_body(writer, self, &bmhd)?; - } + if format.chunky() { + write_chunky_body(writer, self, &bmhd)?; + } else { + write_planar_body(writer, self, &bmhd)?; + } - // add a padding byte (only if necessary) to the body we just finished writing - let mut eof_pos = writer.stream_position()?; - if (eof_pos - body_position) & 1 == 1 { - writer.write_u8(0)?; - eof_pos += 1; - } + // add a padding byte (only if necessary) to the body we just finished writing + let mut eof_pos = writer.stream_position()?; + if (eof_pos - body_position) & 1 == 1 { + writer.write_u8(0)?; + eof_pos += 1; + } - // go back and write out the form chunk header now that we know the final file size - form_chunk.size = (eof_pos - (std::mem::size_of::() as u64 * 2)) as u32; - writer.seek(SeekFrom::Start(form_chunk_position))?; - form_chunk.write(writer)?; + // go back and write out the form chunk header now that we know the final file size + form_chunk.size = (eof_pos - (std::mem::size_of::() as u64 * 2)) as u32; + writer.seek(SeekFrom::Start(form_chunk_position))?; + form_chunk.write(writer)?; - // and then write out the body chunk header since we now know the size of that too - body_chunk_header.size = eof_pos as u32 - std::mem::size_of::() as u32; - writer.seek(SeekFrom::Start(body_position))?; - body_chunk_header.write(writer)?; + // and then write out the body chunk header since we now know the size of that too + body_chunk_header.size = eof_pos as u32 - std::mem::size_of::() as u32; + writer.seek(SeekFrom::Start(body_position))?; + body_chunk_header.write(writer)?; - // and then go back to eof - writer.seek(SeekFrom::Start(eof_pos))?; + // and then go back to eof + writer.seek(SeekFrom::Start(eof_pos))?; - Ok(()) - } + Ok(()) + } - pub fn to_iff_file( - &self, - path: &Path, - palette: &Palette, - format: IffFormat, - ) -> Result<(), IffError> { - let f = File::create(path)?; - let mut writer = BufWriter::new(f); - self.to_iff_bytes(&mut writer, palette, format) - } + pub fn to_iff_file( + &self, + path: &Path, + palette: &Palette, + format: IffFormat, + ) -> Result<(), IffError> { + let f = File::create(path)?; + let mut writer = BufWriter::new(f); + self.to_iff_bytes(&mut writer, palette, format) + } } #[cfg(test)] mod tests { - use tempfile::TempDir; + use tempfile::TempDir; - use super::*; + use super::*; - pub static TEST_BMP_PIXELS_RAW: &[u8] = include_bytes!("../../../test-assets/test_bmp_pixels_raw.bin"); - pub static TEST_LARGE_BMP_PIXELS_RAW: &[u8] = include_bytes!("../../../test-assets/test_large_bmp_pixels_raw.bin"); - pub static TEST_LARGE_BMP_PIXELS_RAW_2: &[u8] = include_bytes!("../../../test-assets/test_large_bmp_pixels_raw2.bin"); + pub static TEST_BMP_PIXELS_RAW: &[u8] = include_bytes!("../../../test-assets/test_bmp_pixels_raw.bin"); + pub static TEST_LARGE_BMP_PIXELS_RAW: &[u8] = include_bytes!("../../../test-assets/test_large_bmp_pixels_raw.bin"); + pub static TEST_LARGE_BMP_PIXELS_RAW_2: &[u8] = include_bytes!("../../../test-assets/test_large_bmp_pixels_raw2.bin"); - #[test] - pub fn load_and_save() -> Result<(), IffError> { - let dp2_palette = - Palette::load_from_file(Path::new("./test-assets/dp2.pal"), PaletteFormat::Normal) - .unwrap(); - let tmp_dir = TempDir::new()?; + #[test] + pub fn load_and_save() -> Result<(), IffError> { + let dp2_palette = + Palette::load_from_file(Path::new("./test-assets/dp2.pal"), PaletteFormat::Normal) + .unwrap(); + let tmp_dir = TempDir::new()?; - // ILBM format + // ILBM format - let (bmp, palette) = Bitmap::load_iff_file(Path::new("./test-assets/test_ilbm.lbm"))?; - assert_eq!(16, bmp.width()); - assert_eq!(16, bmp.height()); - assert_eq!(bmp.pixels(), TEST_BMP_PIXELS_RAW); - assert_eq!(palette, dp2_palette); + let (bmp, palette) = Bitmap::load_iff_file(Path::new("./test-assets/test_ilbm.lbm"))?; + assert_eq!(16, bmp.width()); + assert_eq!(16, bmp.height()); + assert_eq!(bmp.pixels(), TEST_BMP_PIXELS_RAW); + assert_eq!(palette, dp2_palette); - let save_path = tmp_dir.path().join("test_save_ilbm.lbm"); - bmp.to_iff_file(&save_path, &palette, IffFormat::Ilbm)?; - let (reloaded_bmp, reloaded_palette) = Bitmap::load_iff_file(&save_path)?; - assert_eq!(16, reloaded_bmp.width()); - assert_eq!(16, reloaded_bmp.height()); - assert_eq!(reloaded_bmp.pixels(), TEST_BMP_PIXELS_RAW); - assert_eq!(reloaded_palette, dp2_palette); + let save_path = tmp_dir.path().join("test_save_ilbm.lbm"); + bmp.to_iff_file(&save_path, &palette, IffFormat::Ilbm)?; + let (reloaded_bmp, reloaded_palette) = Bitmap::load_iff_file(&save_path)?; + assert_eq!(16, reloaded_bmp.width()); + assert_eq!(16, reloaded_bmp.height()); + assert_eq!(reloaded_bmp.pixels(), TEST_BMP_PIXELS_RAW); + assert_eq!(reloaded_palette, dp2_palette); - // PBM format + // PBM format - let (bmp, palette) = Bitmap::load_iff_file(Path::new("./test-assets/test_pbm.lbm"))?; - assert_eq!(16, bmp.width()); - assert_eq!(16, bmp.height()); - assert_eq!(bmp.pixels(), TEST_BMP_PIXELS_RAW); - assert_eq!(palette, dp2_palette); + let (bmp, palette) = Bitmap::load_iff_file(Path::new("./test-assets/test_pbm.lbm"))?; + assert_eq!(16, bmp.width()); + assert_eq!(16, bmp.height()); + assert_eq!(bmp.pixels(), TEST_BMP_PIXELS_RAW); + assert_eq!(palette, dp2_palette); - let save_path = tmp_dir.path().join("test_save_pbm.lbm"); - bmp.to_iff_file(&save_path, &palette, IffFormat::Pbm)?; - let (reloaded_bmp, reloaded_palette) = Bitmap::load_iff_file(&save_path)?; - assert_eq!(16, reloaded_bmp.width()); - assert_eq!(16, reloaded_bmp.height()); - assert_eq!(reloaded_bmp.pixels(), TEST_BMP_PIXELS_RAW); - assert_eq!(reloaded_palette, dp2_palette); + let save_path = tmp_dir.path().join("test_save_pbm.lbm"); + bmp.to_iff_file(&save_path, &palette, IffFormat::Pbm)?; + let (reloaded_bmp, reloaded_palette) = Bitmap::load_iff_file(&save_path)?; + assert_eq!(16, reloaded_bmp.width()); + assert_eq!(16, reloaded_bmp.height()); + assert_eq!(reloaded_bmp.pixels(), TEST_BMP_PIXELS_RAW); + assert_eq!(reloaded_palette, dp2_palette); - Ok(()) - } + Ok(()) + } - #[test] - pub fn load_and_save_larger_image() -> Result<(), IffError> { - let tmp_dir = TempDir::new()?; + #[test] + pub fn load_and_save_larger_image() -> Result<(), IffError> { + let tmp_dir = TempDir::new()?; - // first image, PBM format + // first image, PBM format - let (bmp, palette) = Bitmap::load_iff_file(Path::new("./test-assets/test_pbm_image.lbm"))?; - assert_eq!(320, bmp.width()); - assert_eq!(200, bmp.height()); - assert_eq!(bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW); + let (bmp, palette) = Bitmap::load_iff_file(Path::new("./test-assets/test_pbm_image.lbm"))?; + assert_eq!(320, bmp.width()); + assert_eq!(200, bmp.height()); + assert_eq!(bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW); - let save_path = tmp_dir.path().join("test_save_pbm_image.lbm"); - bmp.to_iff_file(&save_path, &palette, IffFormat::Pbm)?; - let (reloaded_bmp, _) = Bitmap::load_iff_file(&save_path)?; - assert_eq!(320, reloaded_bmp.width()); - assert_eq!(200, reloaded_bmp.height()); - assert_eq!(reloaded_bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW); + let save_path = tmp_dir.path().join("test_save_pbm_image.lbm"); + bmp.to_iff_file(&save_path, &palette, IffFormat::Pbm)?; + let (reloaded_bmp, _) = Bitmap::load_iff_file(&save_path)?; + assert_eq!(320, reloaded_bmp.width()); + assert_eq!(200, reloaded_bmp.height()); + assert_eq!(reloaded_bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW); - // second image, PBM format + // second image, PBM format - let (bmp, palette) = Bitmap::load_iff_file(Path::new("./test-assets/test_pbm_image2.lbm"))?; - assert_eq!(320, bmp.width()); - assert_eq!(200, bmp.height()); - assert_eq!(bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW_2); + let (bmp, palette) = Bitmap::load_iff_file(Path::new("./test-assets/test_pbm_image2.lbm"))?; + assert_eq!(320, bmp.width()); + assert_eq!(200, bmp.height()); + assert_eq!(bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW_2); - let save_path = tmp_dir.path().join("test_save_pbm_image2.lbm"); - bmp.to_iff_file(&save_path, &palette, IffFormat::Pbm)?; - let (reloaded_bmp, _) = Bitmap::load_iff_file(&save_path)?; - assert_eq!(320, reloaded_bmp.width()); - assert_eq!(200, reloaded_bmp.height()); - assert_eq!(reloaded_bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW_2); + let save_path = tmp_dir.path().join("test_save_pbm_image2.lbm"); + bmp.to_iff_file(&save_path, &palette, IffFormat::Pbm)?; + let (reloaded_bmp, _) = Bitmap::load_iff_file(&save_path)?; + assert_eq!(320, reloaded_bmp.width()); + assert_eq!(200, reloaded_bmp.height()); + assert_eq!(reloaded_bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW_2); - // first image, ILBM format + // first image, ILBM format - let (bmp, palette) = Bitmap::load_iff_file(Path::new("./test-assets/test_ilbm_image.lbm"))?; - assert_eq!(320, bmp.width()); - assert_eq!(200, bmp.height()); - assert_eq!(bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW); + let (bmp, palette) = Bitmap::load_iff_file(Path::new("./test-assets/test_ilbm_image.lbm"))?; + assert_eq!(320, bmp.width()); + assert_eq!(200, bmp.height()); + assert_eq!(bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW); - let save_path = tmp_dir.path().join("test_save_ilbm_image.lbm"); - bmp.to_iff_file(&save_path, &palette, IffFormat::Ilbm)?; - let (reloaded_bmp, _) = Bitmap::load_iff_file(&save_path)?; - assert_eq!(320, reloaded_bmp.width()); - assert_eq!(200, reloaded_bmp.height()); - assert_eq!(reloaded_bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW); + let save_path = tmp_dir.path().join("test_save_ilbm_image.lbm"); + bmp.to_iff_file(&save_path, &palette, IffFormat::Ilbm)?; + let (reloaded_bmp, _) = Bitmap::load_iff_file(&save_path)?; + assert_eq!(320, reloaded_bmp.width()); + assert_eq!(200, reloaded_bmp.height()); + assert_eq!(reloaded_bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW); - // second image, ILBM format + // second image, ILBM format - let (bmp, palette) = Bitmap::load_iff_file(Path::new("./test-assets/test_ilbm_image2.lbm"))?; - assert_eq!(320, bmp.width()); - assert_eq!(200, bmp.height()); - assert_eq!(bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW_2); + let (bmp, palette) = Bitmap::load_iff_file(Path::new("./test-assets/test_ilbm_image2.lbm"))?; + assert_eq!(320, bmp.width()); + assert_eq!(200, bmp.height()); + assert_eq!(bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW_2); - let save_path = tmp_dir.path().join("test_save_ilbm_image2.lbm"); - bmp.to_iff_file(&save_path, &palette, IffFormat::Ilbm)?; - let (reloaded_bmp, _) = Bitmap::load_iff_file(&save_path)?; - assert_eq!(320, reloaded_bmp.width()); - assert_eq!(200, reloaded_bmp.height()); - assert_eq!(reloaded_bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW_2); + let save_path = tmp_dir.path().join("test_save_ilbm_image2.lbm"); + bmp.to_iff_file(&save_path, &palette, IffFormat::Ilbm)?; + let (reloaded_bmp, _) = Bitmap::load_iff_file(&save_path)?; + assert_eq!(320, reloaded_bmp.width()); + assert_eq!(200, reloaded_bmp.height()); + assert_eq!(reloaded_bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW_2); - Ok(()) - } + Ok(()) + } } diff --git a/libretrogd/src/graphics/bitmap/mod.rs b/libretrogd/src/graphics/bitmap/mod.rs index e16f511..20cbad0 100644 --- a/libretrogd/src/graphics/bitmap/mod.rs +++ b/libretrogd/src/graphics/bitmap/mod.rs @@ -21,23 +21,23 @@ pub mod primitives; #[derive(Error, Debug)] pub enum BitmapError { - #[error("Invalid bitmap dimensions")] - InvalidDimensions, + #[error("Invalid bitmap dimensions")] + InvalidDimensions, - #[error("Region is not fully within bitmap boundaries")] - OutOfBounds, + #[error("Region is not fully within bitmap boundaries")] + OutOfBounds, - #[error("Unknown bitmap file type: {0}")] - UnknownFileType(String), + #[error("Unknown bitmap file type: {0}")] + UnknownFileType(String), - #[error("Bitmap IFF file error")] - IffError(#[from] iff::IffError), + #[error("Bitmap IFF file error")] + IffError(#[from] iff::IffError), - #[error("Bitmap PCX file error")] - PcxError(#[from] pcx::PcxError), + #[error("Bitmap PCX file error")] + PcxError(#[from] pcx::PcxError), - #[error("Bitmap GIF file error")] - GifError(#[from] gif::GifError), + #[error("Bitmap GIF file error")] + GifError(#[from] gif::GifError), } /// Container for 256 color 2D pixel/image data that can be rendered to the screen. Pixel data @@ -48,556 +48,556 @@ pub enum BitmapError { /// clipping region is simply not performed / stops at the clipping boundary. #[derive(Clone, Eq, PartialEq)] pub struct Bitmap { - width: u32, - height: u32, - pixels: Box<[u8]>, - clip_region: Rect, + width: u32, + height: u32, + pixels: Box<[u8]>, + clip_region: Rect, } impl std::fmt::Debug for Bitmap { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("Bitmap") - .field("width", &self.width) - .field("height", &self.height) - .field("clip_region", &self.clip_region) - .finish_non_exhaustive() - } + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Bitmap") + .field("width", &self.width) + .field("height", &self.height) + .field("clip_region", &self.clip_region) + .finish_non_exhaustive() + } } impl Bitmap { - /// Creates a new Bitmap with the specified dimensions. - /// - /// # Arguments - /// - /// * `width`: the width of the bitmap in pixels - /// * `height`: the height of the bitmap in pixels - /// - /// returns: `Result` - pub fn new(width: u32, height: u32) -> Result { - if width == 0 || height == 0 { - return Err(BitmapError::InvalidDimensions); - } + /// Creates a new Bitmap with the specified dimensions. + /// + /// # Arguments + /// + /// * `width`: the width of the bitmap in pixels + /// * `height`: the height of the bitmap in pixels + /// + /// returns: `Result` + pub fn new(width: u32, height: u32) -> Result { + if width == 0 || height == 0 { + return Err(BitmapError::InvalidDimensions); + } - Ok(Bitmap { - width, - height, - pixels: vec![0u8; (width * height) as usize].into_boxed_slice(), - clip_region: Rect { - x: 0, - y: 0, - width, - height, - }, - }) - } + Ok(Bitmap { + width, + height, + pixels: vec![0u8; (width * height) as usize].into_boxed_slice(), + clip_region: Rect { + x: 0, + y: 0, + width, + height, + }, + }) + } - /// Creates a new Bitmap, copying the pixel data from a sub-region of another source Bitmap. - /// The resulting bitmap will have dimensions equal to that of the region specified. - /// - /// # Arguments - /// - /// * `source`: the source bitmap to copy from - /// * `region`: the region on the source bitmap to copy from - /// - /// returns: `Result` - pub fn from(source: &Bitmap, region: &Rect) -> Result { - if !source.full_bounds().contains_rect(region) { - return Err(BitmapError::OutOfBounds); - } + /// Creates a new Bitmap, copying the pixel data from a sub-region of another source Bitmap. + /// The resulting bitmap will have dimensions equal to that of the region specified. + /// + /// # Arguments + /// + /// * `source`: the source bitmap to copy from + /// * `region`: the region on the source bitmap to copy from + /// + /// returns: `Result` + pub fn from(source: &Bitmap, region: &Rect) -> Result { + if !source.full_bounds().contains_rect(region) { + return Err(BitmapError::OutOfBounds); + } - let mut bmp = Bitmap::new(region.width, region.height)?; - unsafe { bmp.solid_blit(source, region, 0, 0) }; - Ok(bmp) - } + let mut bmp = Bitmap::new(region.width, region.height)?; + unsafe { bmp.solid_blit(source, region, 0, 0) }; + Ok(bmp) + } - pub fn load_file(path: &Path) -> Result<(Bitmap, Palette), BitmapError> { - if let Some(extension) = path.extension() { - let extension = extension.to_ascii_lowercase(); - match extension.to_str() { - Some("pcx") => Ok(Self::load_pcx_file(path)?), - Some("gif") => Ok(Self::load_gif_file(path)?), - Some("iff") | Some("lbm") | Some("pbm") | Some("bbm") => { - Ok(Self::load_iff_file(path)?) - } - _ => Err(BitmapError::UnknownFileType(String::from( - "Unrecognized file extension", - ))), - } - } else { - Err(BitmapError::UnknownFileType(String::from( - "No file extension", - ))) - } - } + pub fn load_file(path: &Path) -> Result<(Bitmap, Palette), BitmapError> { + if let Some(extension) = path.extension() { + let extension = extension.to_ascii_lowercase(); + match extension.to_str() { + Some("pcx") => Ok(Self::load_pcx_file(path)?), + Some("gif") => Ok(Self::load_gif_file(path)?), + Some("iff") | Some("lbm") | Some("pbm") | Some("bbm") => { + Ok(Self::load_iff_file(path)?) + } + _ => Err(BitmapError::UnknownFileType(String::from( + "Unrecognized file extension", + ))), + } + } else { + Err(BitmapError::UnknownFileType(String::from( + "No file extension", + ))) + } + } - /// Returns the width of the bitmap in pixels. - #[inline] - pub fn width(&self) -> u32 { - self.width - } + /// Returns the width of the bitmap in pixels. + #[inline] + pub fn width(&self) -> u32 { + self.width + } - /// Returns the height of the bitmap in pixels. - #[inline] - pub fn height(&self) -> u32 { - self.height - } + /// Returns the height of the bitmap in pixels. + #[inline] + pub fn height(&self) -> u32 { + self.height + } - /// Returns the right x coordinate of the bitmap. - #[inline] - pub fn right(&self) -> u32 { - self.width - 1 - } + /// Returns the right x coordinate of the bitmap. + #[inline] + pub fn right(&self) -> u32 { + self.width - 1 + } - /// Returns the bottom x coordinate of the bitmap. - #[inline] - pub fn bottom(&self) -> u32 { - self.height - 1 - } + /// Returns the bottom x coordinate of the bitmap. + #[inline] + pub fn bottom(&self) -> u32 { + self.height - 1 + } - /// Returns the current clipping region set on this bitmap. - #[inline] - pub fn clip_region(&self) -> &Rect { - &self.clip_region - } + /// Returns the current clipping region set on this bitmap. + #[inline] + pub fn clip_region(&self) -> &Rect { + &self.clip_region + } - /// Returns a rect representing the full bitmap boundaries, ignoring the current clipping - /// region set on this bitmap. - #[inline] - pub fn full_bounds(&self) -> Rect { - Rect { - x: 0, - y: 0, - width: self.width, - height: self.height, - } - } + /// Returns a rect representing the full bitmap boundaries, ignoring the current clipping + /// region set on this bitmap. + #[inline] + pub fn full_bounds(&self) -> Rect { + Rect { + x: 0, + y: 0, + width: self.width, + height: self.height, + } + } - /// Sets a new clipping region on this bitmap. The region will be automatically clamped to - /// the maximum bitmap boundaries if the supplied region extends beyond it. - /// - /// # Arguments - /// - /// * `region`: the new clipping region - pub fn set_clip_region(&mut self, region: &Rect) { - self.clip_region = *region; - self.clip_region.clamp_to(&self.full_bounds()); - } + /// Sets a new clipping region on this bitmap. The region will be automatically clamped to + /// the maximum bitmap boundaries if the supplied region extends beyond it. + /// + /// # Arguments + /// + /// * `region`: the new clipping region + pub fn set_clip_region(&mut self, region: &Rect) { + self.clip_region = *region; + self.clip_region.clamp_to(&self.full_bounds()); + } - /// Resets the bitmaps clipping region back to the default (full boundaries of the bitmap). - pub fn reset_clip_region(&mut self) { - self.clip_region = self.full_bounds(); - } + /// Resets the bitmaps clipping region back to the default (full boundaries of the bitmap). + pub fn reset_clip_region(&mut self) { + self.clip_region = self.full_bounds(); + } - /// Returns a reference to the raw pixels in this bitmap. - #[inline] - pub fn pixels(&self) -> &[u8] { - &self.pixels - } + /// Returns a reference to the raw pixels in this bitmap. + #[inline] + pub fn pixels(&self) -> &[u8] { + &self.pixels + } - /// Returns a mutable reference to the raw pixels in this bitmap. - #[inline] - pub fn pixels_mut(&mut self) -> &mut [u8] { - &mut self.pixels - } + /// Returns a mutable reference to the raw pixels in this bitmap. + #[inline] + pub fn pixels_mut(&mut self) -> &mut [u8] { + &mut self.pixels + } - /// Returns a reference to the subset of the raw pixels in this bitmap beginning at the - /// given coordinates and extending to the end of the bitmap. If the coordinates given are - /// outside the bitmap's current clipping region, None is returned. - #[inline] - pub fn pixels_at(&self, x: i32, y: i32) -> Option<&[u8]> { - if self.is_xy_visible(x, y) { - let offset = self.get_offset_to_xy(x, y); - Some(&self.pixels[offset..]) - } else { - None - } - } + /// Returns a reference to the subset of the raw pixels in this bitmap beginning at the + /// given coordinates and extending to the end of the bitmap. If the coordinates given are + /// outside the bitmap's current clipping region, None is returned. + #[inline] + pub fn pixels_at(&self, x: i32, y: i32) -> Option<&[u8]> { + if self.is_xy_visible(x, y) { + let offset = self.get_offset_to_xy(x, y); + Some(&self.pixels[offset..]) + } else { + None + } + } - /// Returns a mutable reference to the subset of the raw pixels in this bitmap beginning at the - /// given coordinates and extending to the end of the bitmap. If the coordinates given are - /// outside the bitmap's current clipping region, None is returned. - #[inline] - pub fn pixels_at_mut(&mut self, x: i32, y: i32) -> Option<&mut [u8]> { - if self.is_xy_visible(x, y) { - let offset = self.get_offset_to_xy(x, y); - Some(&mut self.pixels[offset..]) - } else { - None - } - } + /// Returns a mutable reference to the subset of the raw pixels in this bitmap beginning at the + /// given coordinates and extending to the end of the bitmap. If the coordinates given are + /// outside the bitmap's current clipping region, None is returned. + #[inline] + pub fn pixels_at_mut(&mut self, x: i32, y: i32) -> Option<&mut [u8]> { + if self.is_xy_visible(x, y) { + let offset = self.get_offset_to_xy(x, y); + Some(&mut self.pixels[offset..]) + } else { + None + } + } - /// Returns an unsafe reference to the subset of the raw pixels in this bitmap beginning at the - /// given coordinates and extending to the end of the bitmap. The coordinates are not checked - /// for validity, so it is up to you to ensure they lie within the bounds of the bitmap. - #[inline] - pub unsafe fn pixels_at_unchecked(&self, x: i32, y: i32) -> &[u8] { - let offset = self.get_offset_to_xy(x, y); - slice::from_raw_parts(self.pixels.as_ptr().add(offset), self.pixels.len() - offset) - } + /// Returns an unsafe reference to the subset of the raw pixels in this bitmap beginning at the + /// given coordinates and extending to the end of the bitmap. The coordinates are not checked + /// for validity, so it is up to you to ensure they lie within the bounds of the bitmap. + #[inline] + pub unsafe fn pixels_at_unchecked(&self, x: i32, y: i32) -> &[u8] { + let offset = self.get_offset_to_xy(x, y); + slice::from_raw_parts(self.pixels.as_ptr().add(offset), self.pixels.len() - offset) + } - /// Returns a mutable unsafe reference to the subset of the raw pixels in this bitmap beginning - /// at the given coordinates and extending to the end of the bitmap. The coordinates are not - /// checked for validity, so it is up to you to ensure they lie within the bounds of the bitmap. - #[inline] - pub unsafe fn pixels_at_mut_unchecked(&mut self, x: i32, y: i32) -> &mut [u8] { - let offset = self.get_offset_to_xy(x, y); - slice::from_raw_parts_mut( - self.pixels.as_mut_ptr().add(offset), - self.pixels.len() - offset, - ) - } + /// Returns a mutable unsafe reference to the subset of the raw pixels in this bitmap beginning + /// at the given coordinates and extending to the end of the bitmap. The coordinates are not + /// checked for validity, so it is up to you to ensure they lie within the bounds of the bitmap. + #[inline] + pub unsafe fn pixels_at_mut_unchecked(&mut self, x: i32, y: i32) -> &mut [u8] { + let offset = self.get_offset_to_xy(x, y); + slice::from_raw_parts_mut( + self.pixels.as_mut_ptr().add(offset), + self.pixels.len() - offset, + ) + } - /// Returns a pointer to the subset of the raw pixels in this bitmap beginning at the given - /// coordinates. If the coordinates given are outside the bitmap's current clipping region, - /// None is returned. - #[inline] - pub unsafe fn pixels_at_ptr(&self, x: i32, y: i32) -> Option<*const u8> { - if self.is_xy_visible(x, y) { - let offset = self.get_offset_to_xy(x, y); - Some(self.pixels.as_ptr().add(offset)) - } else { - None - } - } + /// Returns a pointer to the subset of the raw pixels in this bitmap beginning at the given + /// coordinates. If the coordinates given are outside the bitmap's current clipping region, + /// None is returned. + #[inline] + pub unsafe fn pixels_at_ptr(&self, x: i32, y: i32) -> Option<*const u8> { + if self.is_xy_visible(x, y) { + let offset = self.get_offset_to_xy(x, y); + Some(self.pixels.as_ptr().add(offset)) + } else { + None + } + } - /// Returns a mutable pointer to the subset of the raw pixels in this bitmap beginning at the - /// given coordinates. If the coordinates given are outside the bitmap's current clipping - /// region, None is returned. - #[inline] - pub unsafe fn pixels_at_mut_ptr(&mut self, x: i32, y: i32) -> Option<*mut u8> { - if self.is_xy_visible(x, y) { - let offset = self.get_offset_to_xy(x, y); - Some(self.pixels.as_mut_ptr().add(offset)) - } else { - None - } - } + /// Returns a mutable pointer to the subset of the raw pixels in this bitmap beginning at the + /// given coordinates. If the coordinates given are outside the bitmap's current clipping + /// region, None is returned. + #[inline] + pub unsafe fn pixels_at_mut_ptr(&mut self, x: i32, y: i32) -> Option<*mut u8> { + if self.is_xy_visible(x, y) { + let offset = self.get_offset_to_xy(x, y); + Some(self.pixels.as_mut_ptr().add(offset)) + } else { + None + } + } - /// Returns an unsafe pointer to the subset of the raw pixels in this bitmap beginning at the - /// given coordinates. The coordinates are not checked for validity, so it is up to you to - /// ensure they lie within the bounds of the bitmap. - #[inline] - pub unsafe fn pixels_at_ptr_unchecked(&self, x: i32, y: i32) -> *const u8 { - let offset = self.get_offset_to_xy(x, y); - self.pixels.as_ptr().add(offset) - } + /// Returns an unsafe pointer to the subset of the raw pixels in this bitmap beginning at the + /// given coordinates. The coordinates are not checked for validity, so it is up to you to + /// ensure they lie within the bounds of the bitmap. + #[inline] + pub unsafe fn pixels_at_ptr_unchecked(&self, x: i32, y: i32) -> *const u8 { + let offset = self.get_offset_to_xy(x, y); + self.pixels.as_ptr().add(offset) + } - /// Returns a mutable unsafe pointer to the subset of the raw pixels in this bitmap beginning - /// at the given coordinates. The coordinates are not checked for validity, so it is up to you - /// to ensure they lie within the bounds of the bitmap. - #[inline] - pub unsafe fn pixels_at_mut_ptr_unchecked(&mut self, x: i32, y: i32) -> *mut u8 { - let offset = self.get_offset_to_xy(x, y); - self.pixels.as_mut_ptr().add(offset) - } + /// Returns a mutable unsafe pointer to the subset of the raw pixels in this bitmap beginning + /// at the given coordinates. The coordinates are not checked for validity, so it is up to you + /// to ensure they lie within the bounds of the bitmap. + #[inline] + pub unsafe fn pixels_at_mut_ptr_unchecked(&mut self, x: i32, y: i32) -> *mut u8 { + let offset = self.get_offset_to_xy(x, y); + self.pixels.as_mut_ptr().add(offset) + } - /// Returns an offset corresponding to the coordinates of the pixel given on this bitmap that - /// can be used with a reference to the raw pixels in this bitmap to access that pixel. The - /// coordinates given are not checked for validity. - #[inline] - pub fn get_offset_to_xy(&self, x: i32, y: i32) -> usize { - ((y * self.width as i32) + x) as usize - } + /// Returns an offset corresponding to the coordinates of the pixel given on this bitmap that + /// can be used with a reference to the raw pixels in this bitmap to access that pixel. The + /// coordinates given are not checked for validity. + #[inline] + pub fn get_offset_to_xy(&self, x: i32, y: i32) -> usize { + ((y * self.width as i32) + x) as usize + } - /// Returns true if the coordinates given lie within the bitmaps clipping region. - #[inline] - pub fn is_xy_visible(&self, x: i32, y: i32) -> bool { - (x >= self.clip_region.x) - && (y >= self.clip_region.y) - && (x <= self.clip_region.right()) - && (y <= self.clip_region.bottom()) - } + /// Returns true if the coordinates given lie within the bitmaps clipping region. + #[inline] + pub fn is_xy_visible(&self, x: i32, y: i32) -> bool { + (x >= self.clip_region.x) + && (y >= self.clip_region.y) + && (x <= self.clip_region.right()) + && (y <= self.clip_region.bottom()) + } - /// Copies and converts the entire pixel data from this bitmap to a destination expecting - /// 32-bit ARGB-format pixel data. This can be used to display the contents of the bitmap - /// on-screen by using an SDL Surface, OpenGL texture, etc as the destination. - /// - /// # Arguments - /// - /// * `dest`: destination 32-bit ARGB pixel buffer to copy converted pixels to - /// * `palette`: the 256 colour palette to use during pixel conversion - pub fn copy_as_argb_to(&self, dest: &mut [u32], palette: &Palette) { - for (src, dest) in self.pixels().iter().zip(dest.iter_mut()) { - *dest = palette[*src]; - } - } + /// Copies and converts the entire pixel data from this bitmap to a destination expecting + /// 32-bit ARGB-format pixel data. This can be used to display the contents of the bitmap + /// on-screen by using an SDL Surface, OpenGL texture, etc as the destination. + /// + /// # Arguments + /// + /// * `dest`: destination 32-bit ARGB pixel buffer to copy converted pixels to + /// * `palette`: the 256 colour palette to use during pixel conversion + pub fn copy_as_argb_to(&self, dest: &mut [u32], palette: &Palette) { + for (src, dest) in self.pixels().iter().zip(dest.iter_mut()) { + *dest = palette[*src]; + } + } } #[cfg(test)] pub mod tests { - use claim::assert_matches; + use claim::assert_matches; - use super::*; + use super::*; - #[rustfmt::skip] - static RAW_BMP_PIXELS: &[u8] = &[ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 2, - ]; + #[rustfmt::skip] + static RAW_BMP_PIXELS: &[u8] = &[ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 2, + ]; - #[rustfmt::skip] - static RAW_BMP_PIXELS_SUBSET: &[u8] = &[ - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 2, - ]; + #[rustfmt::skip] + static RAW_BMP_PIXELS_SUBSET: &[u8] = &[ + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 2, + ]; - #[test] - pub fn creation_and_sizing() { - assert_matches!(Bitmap::new(0, 0), Err(BitmapError::InvalidDimensions)); - assert_matches!(Bitmap::new(16, 0), Err(BitmapError::InvalidDimensions)); - assert_matches!(Bitmap::new(0, 32), Err(BitmapError::InvalidDimensions)); - let bmp = Bitmap::new(16, 32).unwrap(); - assert_eq!(16, bmp.width()); - assert_eq!(32, bmp.height()); - assert_eq!(15, bmp.right()); - assert_eq!(31, bmp.bottom()); - assert_eq!( - Rect { - x: 0, - y: 0, - width: 16, - height: 32 - }, - bmp.full_bounds() - ); - assert_eq!( - Rect { - x: 0, - y: 0, - width: 16, - height: 32 - }, - *bmp.clip_region() - ); - } + #[test] + pub fn creation_and_sizing() { + assert_matches!(Bitmap::new(0, 0), Err(BitmapError::InvalidDimensions)); + assert_matches!(Bitmap::new(16, 0), Err(BitmapError::InvalidDimensions)); + assert_matches!(Bitmap::new(0, 32), Err(BitmapError::InvalidDimensions)); + let bmp = Bitmap::new(16, 32).unwrap(); + assert_eq!(16, bmp.width()); + assert_eq!(32, bmp.height()); + assert_eq!(15, bmp.right()); + assert_eq!(31, bmp.bottom()); + assert_eq!( + Rect { + x: 0, + y: 0, + width: 16, + height: 32, + }, + bmp.full_bounds() + ); + assert_eq!( + Rect { + x: 0, + y: 0, + width: 16, + height: 32, + }, + *bmp.clip_region() + ); + } - #[test] - pub fn copy_from() { - let mut bmp = Bitmap::new(8, 8).unwrap(); - bmp.pixels_mut().copy_from_slice(RAW_BMP_PIXELS); + #[test] + pub fn copy_from() { + let mut bmp = Bitmap::new(8, 8).unwrap(); + bmp.pixels_mut().copy_from_slice(RAW_BMP_PIXELS); - assert_matches!( + assert_matches!( Bitmap::from(&bmp, &Rect::new(0, 0, 16, 16)), Err(BitmapError::OutOfBounds) ); - let copy = Bitmap::from(&bmp, &Rect::new(0, 0, 8, 8)).unwrap(); - assert_eq!(bmp.pixels(), copy.pixels()); + let copy = Bitmap::from(&bmp, &Rect::new(0, 0, 8, 8)).unwrap(); + assert_eq!(bmp.pixels(), copy.pixels()); - let copy = Bitmap::from(&bmp, &Rect::new(4, 4, 4, 4)).unwrap(); - assert_eq!(RAW_BMP_PIXELS_SUBSET, copy.pixels()); - } + let copy = Bitmap::from(&bmp, &Rect::new(4, 4, 4, 4)).unwrap(); + assert_eq!(RAW_BMP_PIXELS_SUBSET, copy.pixels()); + } - #[test] - pub fn xy_offset_calculation() { - let bmp = Bitmap::new(20, 15).unwrap(); - assert_eq!(0, bmp.get_offset_to_xy(0, 0)); - assert_eq!(19, bmp.get_offset_to_xy(19, 0)); - assert_eq!(20, bmp.get_offset_to_xy(0, 1)); - assert_eq!(280, bmp.get_offset_to_xy(0, 14)); - assert_eq!(299, bmp.get_offset_to_xy(19, 14)); - assert_eq!(227, bmp.get_offset_to_xy(7, 11)); - } + #[test] + pub fn xy_offset_calculation() { + let bmp = Bitmap::new(20, 15).unwrap(); + assert_eq!(0, bmp.get_offset_to_xy(0, 0)); + assert_eq!(19, bmp.get_offset_to_xy(19, 0)); + assert_eq!(20, bmp.get_offset_to_xy(0, 1)); + assert_eq!(280, bmp.get_offset_to_xy(0, 14)); + assert_eq!(299, bmp.get_offset_to_xy(19, 14)); + assert_eq!(227, bmp.get_offset_to_xy(7, 11)); + } - #[test] - pub fn bounds_testing_and_clip_region() { - let mut bmp = Bitmap::new(16, 8).unwrap(); - assert!(bmp.is_xy_visible(0, 0)); - assert!(bmp.is_xy_visible(15, 0)); - assert!(bmp.is_xy_visible(0, 7)); - assert!(bmp.is_xy_visible(15, 7)); - assert!(!bmp.is_xy_visible(-1, -1)); - assert!(!bmp.is_xy_visible(16, 8)); - assert!(!bmp.is_xy_visible(4, -2)); - assert!(!bmp.is_xy_visible(11, 8)); - assert!(!bmp.is_xy_visible(-1, 3)); - assert!(!bmp.is_xy_visible(16, 6)); + #[test] + pub fn bounds_testing_and_clip_region() { + let mut bmp = Bitmap::new(16, 8).unwrap(); + assert!(bmp.is_xy_visible(0, 0)); + assert!(bmp.is_xy_visible(15, 0)); + assert!(bmp.is_xy_visible(0, 7)); + assert!(bmp.is_xy_visible(15, 7)); + assert!(!bmp.is_xy_visible(-1, -1)); + assert!(!bmp.is_xy_visible(16, 8)); + assert!(!bmp.is_xy_visible(4, -2)); + assert!(!bmp.is_xy_visible(11, 8)); + assert!(!bmp.is_xy_visible(-1, 3)); + assert!(!bmp.is_xy_visible(16, 6)); - let new_clip_region = Rect::from_coords(4, 2, 12, 6); - bmp.set_clip_region(&new_clip_region); - assert_eq!( - Rect { - x: 0, - y: 0, - width: 16, - height: 8 - }, - bmp.full_bounds() - ); - assert_eq!(new_clip_region, *bmp.clip_region()); - assert!(bmp.is_xy_visible(4, 2)); - assert!(bmp.is_xy_visible(12, 2)); - assert!(bmp.is_xy_visible(4, 6)); - assert!(bmp.is_xy_visible(12, 6)); - assert!(!bmp.is_xy_visible(3, 1)); - assert!(!bmp.is_xy_visible(13, 7)); - assert!(!bmp.is_xy_visible(5, 1)); - assert!(!bmp.is_xy_visible(10, 7)); - assert!(!bmp.is_xy_visible(3, 4)); - assert!(!bmp.is_xy_visible(13, 5)); + let new_clip_region = Rect::from_coords(4, 2, 12, 6); + bmp.set_clip_region(&new_clip_region); + assert_eq!( + Rect { + x: 0, + y: 0, + width: 16, + height: 8, + }, + bmp.full_bounds() + ); + assert_eq!(new_clip_region, *bmp.clip_region()); + assert!(bmp.is_xy_visible(4, 2)); + assert!(bmp.is_xy_visible(12, 2)); + assert!(bmp.is_xy_visible(4, 6)); + assert!(bmp.is_xy_visible(12, 6)); + assert!(!bmp.is_xy_visible(3, 1)); + assert!(!bmp.is_xy_visible(13, 7)); + assert!(!bmp.is_xy_visible(5, 1)); + assert!(!bmp.is_xy_visible(10, 7)); + assert!(!bmp.is_xy_visible(3, 4)); + assert!(!bmp.is_xy_visible(13, 5)); - assert!(!bmp.is_xy_visible(0, 0)); - assert!(!bmp.is_xy_visible(15, 0)); - assert!(!bmp.is_xy_visible(0, 7)); - assert!(!bmp.is_xy_visible(15, 7)); - bmp.reset_clip_region(); - assert!(bmp.is_xy_visible(0, 0)); - assert!(bmp.is_xy_visible(15, 0)); - assert!(bmp.is_xy_visible(0, 7)); - assert!(bmp.is_xy_visible(15, 7)); - assert!(!bmp.is_xy_visible(-1, -1)); - assert!(!bmp.is_xy_visible(16, 8)); - } + assert!(!bmp.is_xy_visible(0, 0)); + assert!(!bmp.is_xy_visible(15, 0)); + assert!(!bmp.is_xy_visible(0, 7)); + assert!(!bmp.is_xy_visible(15, 7)); + bmp.reset_clip_region(); + assert!(bmp.is_xy_visible(0, 0)); + assert!(bmp.is_xy_visible(15, 0)); + assert!(bmp.is_xy_visible(0, 7)); + assert!(bmp.is_xy_visible(15, 7)); + assert!(!bmp.is_xy_visible(-1, -1)); + assert!(!bmp.is_xy_visible(16, 8)); + } - #[test] - pub fn pixels_at() { - let mut bmp = Bitmap::new(8, 8).unwrap(); - bmp.pixels_mut().copy_from_slice(RAW_BMP_PIXELS); + #[test] + pub fn pixels_at() { + let mut bmp = Bitmap::new(8, 8).unwrap(); + bmp.pixels_mut().copy_from_slice(RAW_BMP_PIXELS); - assert_eq!(None, bmp.pixels_at(-1, -1)); + assert_eq!(None, bmp.pixels_at(-1, -1)); - let offset = bmp.get_offset_to_xy(1, 1); - let pixels = bmp.pixels_at(0, 0).unwrap(); - assert_eq!(64, pixels.len()); - assert_eq!(0, pixels[0]); - assert_eq!(1, pixels[offset]); - assert_eq!(2, pixels[63]); + let offset = bmp.get_offset_to_xy(1, 1); + let pixels = bmp.pixels_at(0, 0).unwrap(); + assert_eq!(64, pixels.len()); + assert_eq!(0, pixels[0]); + assert_eq!(1, pixels[offset]); + assert_eq!(2, pixels[63]); - let pixels = bmp.pixels_at(1, 1).unwrap(); - assert_eq!(55, pixels.len()); - assert_eq!(1, pixels[0]); - assert_eq!(2, pixels[54]); - } + let pixels = bmp.pixels_at(1, 1).unwrap(); + assert_eq!(55, pixels.len()); + assert_eq!(1, pixels[0]); + assert_eq!(2, pixels[54]); + } - #[test] - pub fn pixels_at_mut() { - let mut bmp = Bitmap::new(8, 8).unwrap(); - bmp.pixels_mut().copy_from_slice(RAW_BMP_PIXELS); + #[test] + pub fn pixels_at_mut() { + let mut bmp = Bitmap::new(8, 8).unwrap(); + bmp.pixels_mut().copy_from_slice(RAW_BMP_PIXELS); - assert_eq!(None, bmp.pixels_at_mut(-1, -1)); + assert_eq!(None, bmp.pixels_at_mut(-1, -1)); - let offset = bmp.get_offset_to_xy(1, 1); - let pixels = bmp.pixels_at_mut(0, 0).unwrap(); - assert_eq!(64, pixels.len()); - assert_eq!(0, pixels[0]); - assert_eq!(1, pixels[offset]); - assert_eq!(2, pixels[63]); + let offset = bmp.get_offset_to_xy(1, 1); + let pixels = bmp.pixels_at_mut(0, 0).unwrap(); + assert_eq!(64, pixels.len()); + assert_eq!(0, pixels[0]); + assert_eq!(1, pixels[offset]); + assert_eq!(2, pixels[63]); - let pixels = bmp.pixels_at_mut(1, 1).unwrap(); - assert_eq!(55, pixels.len()); - assert_eq!(1, pixels[0]); - assert_eq!(2, pixels[54]); - } + let pixels = bmp.pixels_at_mut(1, 1).unwrap(); + assert_eq!(55, pixels.len()); + assert_eq!(1, pixels[0]); + assert_eq!(2, pixels[54]); + } - #[test] - pub fn pixels_at_unchecked() { - let mut bmp = Bitmap::new(8, 8).unwrap(); - bmp.pixels_mut().copy_from_slice(RAW_BMP_PIXELS); + #[test] + pub fn pixels_at_unchecked() { + let mut bmp = Bitmap::new(8, 8).unwrap(); + bmp.pixels_mut().copy_from_slice(RAW_BMP_PIXELS); - let offset = bmp.get_offset_to_xy(1, 1); - let pixels = unsafe { bmp.pixels_at_unchecked(0, 0) }; - assert_eq!(64, pixels.len()); - assert_eq!(0, pixels[0]); - assert_eq!(1, pixels[offset]); - assert_eq!(2, pixels[63]); + let offset = bmp.get_offset_to_xy(1, 1); + let pixels = unsafe { bmp.pixels_at_unchecked(0, 0) }; + assert_eq!(64, pixels.len()); + assert_eq!(0, pixels[0]); + assert_eq!(1, pixels[offset]); + assert_eq!(2, pixels[63]); - let pixels = unsafe { bmp.pixels_at_unchecked(1, 1) }; - assert_eq!(55, pixels.len()); - assert_eq!(1, pixels[0]); - assert_eq!(2, pixels[54]); - } + let pixels = unsafe { bmp.pixels_at_unchecked(1, 1) }; + assert_eq!(55, pixels.len()); + assert_eq!(1, pixels[0]); + assert_eq!(2, pixels[54]); + } - #[test] - pub fn pixels_at_mut_unchecked() { - let mut bmp = Bitmap::new(8, 8).unwrap(); - bmp.pixels_mut().copy_from_slice(RAW_BMP_PIXELS); + #[test] + pub fn pixels_at_mut_unchecked() { + let mut bmp = Bitmap::new(8, 8).unwrap(); + bmp.pixels_mut().copy_from_slice(RAW_BMP_PIXELS); - let offset = bmp.get_offset_to_xy(1, 1); - let pixels = unsafe { bmp.pixels_at_mut_unchecked(0, 0) }; - assert_eq!(64, pixels.len()); - assert_eq!(0, pixels[0]); - assert_eq!(1, pixels[offset]); - assert_eq!(2, pixels[63]); + let offset = bmp.get_offset_to_xy(1, 1); + let pixels = unsafe { bmp.pixels_at_mut_unchecked(0, 0) }; + assert_eq!(64, pixels.len()); + assert_eq!(0, pixels[0]); + assert_eq!(1, pixels[offset]); + assert_eq!(2, pixels[63]); - let pixels = unsafe { bmp.pixels_at_mut_unchecked(1, 1) }; - assert_eq!(55, pixels.len()); - assert_eq!(1, pixels[0]); - assert_eq!(2, pixels[54]); - } + let pixels = unsafe { bmp.pixels_at_mut_unchecked(1, 1) }; + assert_eq!(55, pixels.len()); + assert_eq!(1, pixels[0]); + assert_eq!(2, pixels[54]); + } - #[test] - pub fn pixels_at_ptr() { - let mut bmp = Bitmap::new(8, 8).unwrap(); - bmp.pixels_mut().copy_from_slice(RAW_BMP_PIXELS); + #[test] + pub fn pixels_at_ptr() { + let mut bmp = Bitmap::new(8, 8).unwrap(); + bmp.pixels_mut().copy_from_slice(RAW_BMP_PIXELS); - assert_eq!(None, unsafe { bmp.pixels_at_ptr(-1, -1) }); + assert_eq!(None, unsafe { bmp.pixels_at_ptr(-1, -1) }); - let offset = bmp.get_offset_to_xy(1, 1); - let pixels = unsafe { bmp.pixels_at_ptr(0, 0).unwrap() }; - assert_eq!(0, unsafe { *pixels }); - assert_eq!(1, unsafe { *(pixels.add(offset)) }); - assert_eq!(2, unsafe { *(pixels.add(63)) }); + let offset = bmp.get_offset_to_xy(1, 1); + let pixels = unsafe { bmp.pixels_at_ptr(0, 0).unwrap() }; + assert_eq!(0, unsafe { *pixels }); + assert_eq!(1, unsafe { *(pixels.add(offset)) }); + assert_eq!(2, unsafe { *(pixels.add(63)) }); - let pixels = unsafe { bmp.pixels_at_ptr(1, 1).unwrap() }; - assert_eq!(1, unsafe { *pixels }); - assert_eq!(2, unsafe { *(pixels.add(54)) }); - } + let pixels = unsafe { bmp.pixels_at_ptr(1, 1).unwrap() }; + assert_eq!(1, unsafe { *pixels }); + assert_eq!(2, unsafe { *(pixels.add(54)) }); + } - #[test] - pub fn pixels_at_mut_ptr() { - let mut bmp = Bitmap::new(8, 8).unwrap(); - bmp.pixels_mut().copy_from_slice(RAW_BMP_PIXELS); + #[test] + pub fn pixels_at_mut_ptr() { + let mut bmp = Bitmap::new(8, 8).unwrap(); + bmp.pixels_mut().copy_from_slice(RAW_BMP_PIXELS); - assert_eq!(None, unsafe { bmp.pixels_at_mut_ptr(-1, -1) }); + assert_eq!(None, unsafe { bmp.pixels_at_mut_ptr(-1, -1) }); - let offset = bmp.get_offset_to_xy(1, 1); - let pixels = unsafe { bmp.pixels_at_mut_ptr(0, 0).unwrap() }; - assert_eq!(0, unsafe { *pixels }); - assert_eq!(1, unsafe { *(pixels.add(offset)) }); - assert_eq!(2, unsafe { *(pixels.add(63)) }); + let offset = bmp.get_offset_to_xy(1, 1); + let pixels = unsafe { bmp.pixels_at_mut_ptr(0, 0).unwrap() }; + assert_eq!(0, unsafe { *pixels }); + assert_eq!(1, unsafe { *(pixels.add(offset)) }); + assert_eq!(2, unsafe { *(pixels.add(63)) }); - let pixels = unsafe { bmp.pixels_at_mut_ptr(1, 1).unwrap() }; - assert_eq!(1, unsafe { *pixels }); - assert_eq!(2, unsafe { *(pixels.add(54)) }); - } + let pixels = unsafe { bmp.pixels_at_mut_ptr(1, 1).unwrap() }; + assert_eq!(1, unsafe { *pixels }); + assert_eq!(2, unsafe { *(pixels.add(54)) }); + } - #[test] - pub fn pixels_at_ptr_unchecked() { - let mut bmp = Bitmap::new(8, 8).unwrap(); - bmp.pixels_mut().copy_from_slice(RAW_BMP_PIXELS); + #[test] + pub fn pixels_at_ptr_unchecked() { + let mut bmp = Bitmap::new(8, 8).unwrap(); + bmp.pixels_mut().copy_from_slice(RAW_BMP_PIXELS); - let offset = bmp.get_offset_to_xy(1, 1); - let pixels = unsafe { bmp.pixels_at_ptr_unchecked(0, 0) }; - assert_eq!(0, unsafe { *pixels }); - assert_eq!(1, unsafe { *(pixels.add(offset)) }); - assert_eq!(2, unsafe { *(pixels.add(63)) }); + let offset = bmp.get_offset_to_xy(1, 1); + let pixels = unsafe { bmp.pixels_at_ptr_unchecked(0, 0) }; + assert_eq!(0, unsafe { *pixels }); + assert_eq!(1, unsafe { *(pixels.add(offset)) }); + assert_eq!(2, unsafe { *(pixels.add(63)) }); - let pixels = unsafe { bmp.pixels_at_ptr_unchecked(1, 1) }; - assert_eq!(1, unsafe { *pixels }); - assert_eq!(2, unsafe { *(pixels.add(54)) }); - } + let pixels = unsafe { bmp.pixels_at_ptr_unchecked(1, 1) }; + assert_eq!(1, unsafe { *pixels }); + assert_eq!(2, unsafe { *(pixels.add(54)) }); + } - #[test] - pub fn pixels_at_mut_ptr_unchecked() { - let mut bmp = Bitmap::new(8, 8).unwrap(); - bmp.pixels_mut().copy_from_slice(RAW_BMP_PIXELS); + #[test] + pub fn pixels_at_mut_ptr_unchecked() { + let mut bmp = Bitmap::new(8, 8).unwrap(); + bmp.pixels_mut().copy_from_slice(RAW_BMP_PIXELS); - let offset = bmp.get_offset_to_xy(1, 1); - let pixels = unsafe { bmp.pixels_at_mut_ptr_unchecked(0, 0) }; - assert_eq!(0, unsafe { *pixels }); - assert_eq!(1, unsafe { *(pixels.add(offset)) }); - assert_eq!(2, unsafe { *(pixels.add(63)) }); + let offset = bmp.get_offset_to_xy(1, 1); + let pixels = unsafe { bmp.pixels_at_mut_ptr_unchecked(0, 0) }; + assert_eq!(0, unsafe { *pixels }); + assert_eq!(1, unsafe { *(pixels.add(offset)) }); + assert_eq!(2, unsafe { *(pixels.add(63)) }); - let pixels = unsafe { bmp.pixels_at_mut_ptr_unchecked(1, 1) }; - assert_eq!(1, unsafe { *pixels }); - assert_eq!(2, unsafe { *(pixels.add(54)) }); - } + let pixels = unsafe { bmp.pixels_at_mut_ptr_unchecked(1, 1) }; + assert_eq!(1, unsafe { *pixels }); + assert_eq!(2, unsafe { *(pixels.add(54)) }); + } } diff --git a/libretrogd/src/graphics/bitmap/pcx.rs b/libretrogd/src/graphics/bitmap/pcx.rs index b323602..dc0da5a 100644 --- a/libretrogd/src/graphics/bitmap/pcx.rs +++ b/libretrogd/src/graphics/bitmap/pcx.rs @@ -10,340 +10,340 @@ use crate::utils::bytes::ReadFixedLengthByteArray; #[derive(Error, Debug)] pub enum PcxError { - #[error("Bad or unsupported PCX file: {0}")] - BadFile(String), + #[error("Bad or unsupported PCX file: {0}")] + BadFile(String), - #[error("PCX palette data error")] - BadPalette(#[from] PaletteError), + #[error("PCX palette data error")] + BadPalette(#[from] PaletteError), - #[error("PCX I/O error")] - IOError(#[from] std::io::Error), + #[error("PCX I/O error")] + IOError(#[from] std::io::Error), } #[derive(Debug, Copy, Clone)] #[repr(packed)] struct PcxHeader { - manufacturer: u8, - version: u8, - encoding: u8, - bpp: u8, - x1: u16, - y1: u16, - x2: u16, - y2: u16, - horizontal_dpi: u16, - vertical_dpi: u16, - ega_palette: [u8; 48], - reserved: u8, - num_color_planes: u8, - bytes_per_line: u16, - palette_type: u16, - horizontal_size: u16, - vertical_size: u16, - padding: [u8; 54], + manufacturer: u8, + version: u8, + encoding: u8, + bpp: u8, + x1: u16, + y1: u16, + x2: u16, + y2: u16, + horizontal_dpi: u16, + vertical_dpi: u16, + ega_palette: [u8; 48], + reserved: u8, + num_color_planes: u8, + bytes_per_line: u16, + palette_type: u16, + horizontal_size: u16, + vertical_size: u16, + padding: [u8; 54], } impl PcxHeader { - pub fn read(reader: &mut T) -> Result { - Ok(PcxHeader { - manufacturer: reader.read_u8()?, - version: reader.read_u8()?, - encoding: reader.read_u8()?, - bpp: reader.read_u8()?, - x1: reader.read_u16::()?, - y1: reader.read_u16::()?, - x2: reader.read_u16::()?, - y2: reader.read_u16::()?, - horizontal_dpi: reader.read_u16::()?, - vertical_dpi: reader.read_u16::()?, - ega_palette: reader.read_bytes()?, - reserved: reader.read_u8()?, - num_color_planes: reader.read_u8()?, - bytes_per_line: reader.read_u16::()?, - palette_type: reader.read_u16::()?, - horizontal_size: reader.read_u16::()?, - vertical_size: reader.read_u16::()?, - padding: reader.read_bytes()?, - }) - } + pub fn read(reader: &mut T) -> Result { + Ok(PcxHeader { + manufacturer: reader.read_u8()?, + version: reader.read_u8()?, + encoding: reader.read_u8()?, + bpp: reader.read_u8()?, + x1: reader.read_u16::()?, + y1: reader.read_u16::()?, + x2: reader.read_u16::()?, + y2: reader.read_u16::()?, + horizontal_dpi: reader.read_u16::()?, + vertical_dpi: reader.read_u16::()?, + ega_palette: reader.read_bytes()?, + reserved: reader.read_u8()?, + num_color_planes: reader.read_u8()?, + bytes_per_line: reader.read_u16::()?, + palette_type: reader.read_u16::()?, + horizontal_size: reader.read_u16::()?, + vertical_size: reader.read_u16::()?, + padding: reader.read_bytes()?, + }) + } - pub fn write(&self, writer: &mut T) -> Result<(), PcxError> { - writer.write_u8(self.manufacturer)?; - writer.write_u8(self.version)?; - writer.write_u8(self.encoding)?; - writer.write_u8(self.bpp)?; - writer.write_u16::(self.x1)?; - writer.write_u16::(self.y1)?; - writer.write_u16::(self.x2)?; - writer.write_u16::(self.y2)?; - writer.write_u16::(self.horizontal_dpi)?; - writer.write_u16::(self.vertical_dpi)?; - writer.write_all(&self.ega_palette)?; - writer.write_u8(self.reserved)?; - writer.write_u8(self.num_color_planes)?; - writer.write_u16::(self.bytes_per_line)?; - writer.write_u16::(self.palette_type)?; - writer.write_u16::(self.horizontal_size)?; - writer.write_u16::(self.vertical_size)?; - writer.write_all(&self.padding)?; - Ok(()) - } + pub fn write(&self, writer: &mut T) -> Result<(), PcxError> { + writer.write_u8(self.manufacturer)?; + writer.write_u8(self.version)?; + writer.write_u8(self.encoding)?; + writer.write_u8(self.bpp)?; + writer.write_u16::(self.x1)?; + writer.write_u16::(self.y1)?; + writer.write_u16::(self.x2)?; + writer.write_u16::(self.y2)?; + writer.write_u16::(self.horizontal_dpi)?; + writer.write_u16::(self.vertical_dpi)?; + writer.write_all(&self.ega_palette)?; + writer.write_u8(self.reserved)?; + writer.write_u8(self.num_color_planes)?; + writer.write_u16::(self.bytes_per_line)?; + writer.write_u16::(self.palette_type)?; + writer.write_u16::(self.horizontal_size)?; + writer.write_u16::(self.vertical_size)?; + writer.write_all(&self.padding)?; + Ok(()) + } } fn write_pcx_data( - writer: &mut T, - run_count: u8, - pixel: u8, + writer: &mut T, + run_count: u8, + pixel: u8, ) -> Result<(), PcxError> { - if (run_count > 1) || ((pixel & 0xc0) == 0xc0) { - writer.write_u8(0xc0 | run_count)?; - } - writer.write_u8(pixel)?; - Ok(()) + if (run_count > 1) || ((pixel & 0xc0) == 0xc0) { + writer.write_u8(0xc0 | run_count)?; + } + writer.write_u8(pixel)?; + Ok(()) } impl Bitmap { - pub fn load_pcx_bytes( - reader: &mut T, - ) -> Result<(Bitmap, Palette), PcxError> { - let header = PcxHeader::read(reader)?; + pub fn load_pcx_bytes( + reader: &mut T, + ) -> Result<(Bitmap, Palette), PcxError> { + let header = PcxHeader::read(reader)?; - if header.manufacturer != 10 { - return Err(PcxError::BadFile(String::from( - "Unexpected header.manufacturer value, probably not a PCX file", - ))); - } - if header.version != 5 { - return Err(PcxError::BadFile(String::from( - "Only version 5 PCX files are supported", - ))); - } - if header.encoding != 1 { - return Err(PcxError::BadFile(String::from( - "Only RLE-compressed PCX files are supported", - ))); - } - if header.bpp != 8 { - return Err(PcxError::BadFile(String::from( - "Only 8-bit indexed (256 color palette) PCX files are supported", - ))); - } - if header.x2 == 0 || header.y2 == 0 { - return Err(PcxError::BadFile(String::from( - "Invalid PCX image dimensions", - ))); - } + if header.manufacturer != 10 { + return Err(PcxError::BadFile(String::from( + "Unexpected header.manufacturer value, probably not a PCX file", + ))); + } + if header.version != 5 { + return Err(PcxError::BadFile(String::from( + "Only version 5 PCX files are supported", + ))); + } + if header.encoding != 1 { + return Err(PcxError::BadFile(String::from( + "Only RLE-compressed PCX files are supported", + ))); + } + if header.bpp != 8 { + return Err(PcxError::BadFile(String::from( + "Only 8-bit indexed (256 color palette) PCX files are supported", + ))); + } + if header.x2 == 0 || header.y2 == 0 { + return Err(PcxError::BadFile(String::from( + "Invalid PCX image dimensions", + ))); + } - // read the PCX file's pixel data into a bitmap + // read the PCX file's pixel data into a bitmap - let width = (header.x2 + 1) as u32; - let height = (header.y2 + 1) as u32; - let mut bmp = Bitmap::new(width, height).unwrap(); - let mut writer = Cursor::new(bmp.pixels_mut()); + let width = (header.x2 + 1) as u32; + let height = (header.y2 + 1) as u32; + let mut bmp = Bitmap::new(width, height).unwrap(); + let mut writer = Cursor::new(bmp.pixels_mut()); - for _y in 0..height { - // read the next scanline's worth of pixels from the PCX file - let mut x: u32 = 0; - while x < (header.bytes_per_line as u32) { - let mut data: u8; - let mut count: u32; + for _y in 0..height { + // read the next scanline's worth of pixels from the PCX file + let mut x: u32 = 0; + while x < (header.bytes_per_line as u32) { + let mut data: u8; + let mut count: u32; - // read pixel or RLE count - data = reader.read_u8()?; + // read pixel or RLE count + data = reader.read_u8()?; - if (data & 0xc0) == 0xc0 { - // it was an RLE count, actual pixel is the next byte ... - count = (data & 0x3f) as u32; - data = reader.read_u8()?; - } else { - // it was just a single pixel - count = 1; - } + if (data & 0xc0) == 0xc0 { + // it was an RLE count, actual pixel is the next byte ... + count = (data & 0x3f) as u32; + data = reader.read_u8()?; + } else { + // it was just a single pixel + count = 1; + } - // write the current pixel value 'data' to the bitmap 'count' number of times - while count > 0 { - if x <= width { - writer.write_u8(data)?; - } else { - writer.seek(SeekFrom::Current(1))?; - } + // write the current pixel value 'data' to the bitmap 'count' number of times + while count > 0 { + if x <= width { + writer.write_u8(data)?; + } else { + writer.seek(SeekFrom::Current(1))?; + } - x += 1; - count -= 1; - } - } - } + x += 1; + count -= 1; + } + } + } - // now read the palette data located at the end of the PCX file - // palette data should be for 256 colors, 3 bytes per color = 768 bytes - // the palette is preceded by a single byte, 0x0c, which we will also validate + // now read the palette data located at the end of the PCX file + // palette data should be for 256 colors, 3 bytes per color = 768 bytes + // the palette is preceded by a single byte, 0x0c, which we will also validate - reader.seek(SeekFrom::End(-769))?; + reader.seek(SeekFrom::End(-769))?; - let palette_marker = reader.read_u8()?; - if palette_marker != 0x0c { - return Err(PcxError::BadFile(String::from( - "Palette not found at end of file", - ))); - } + let palette_marker = reader.read_u8()?; + if palette_marker != 0x0c { + return Err(PcxError::BadFile(String::from( + "Palette not found at end of file", + ))); + } - let palette = Palette::load_from_bytes(reader, PaletteFormat::Normal)?; + let palette = Palette::load_from_bytes(reader, PaletteFormat::Normal)?; - Ok((bmp, palette)) - } + Ok((bmp, palette)) + } - pub fn load_pcx_file(path: &Path) -> Result<(Bitmap, Palette), PcxError> { - let f = File::open(path)?; - let mut reader = BufReader::new(f); - Self::load_pcx_bytes(&mut reader) - } + pub fn load_pcx_file(path: &Path) -> Result<(Bitmap, Palette), PcxError> { + let f = File::open(path)?; + let mut reader = BufReader::new(f); + Self::load_pcx_bytes(&mut reader) + } - pub fn to_pcx_bytes( - &self, - writer: &mut T, - palette: &Palette, - ) -> Result<(), PcxError> { - let header = PcxHeader { - manufacturer: 10, - version: 5, - encoding: 1, - bpp: 8, - x1: 0, - y1: 0, - x2: self.right() as u16, - y2: self.bottom() as u16, - horizontal_dpi: 320, - vertical_dpi: 200, - ega_palette: [0u8; 48], - reserved: 0, - num_color_planes: 1, - bytes_per_line: self.width() as u16, - palette_type: 1, - horizontal_size: self.width() as u16, - vertical_size: self.height() as u16, - padding: [0u8; 54], - }; - header.write(writer)?; + pub fn to_pcx_bytes( + &self, + writer: &mut T, + palette: &Palette, + ) -> Result<(), PcxError> { + let header = PcxHeader { + manufacturer: 10, + version: 5, + encoding: 1, + bpp: 8, + x1: 0, + y1: 0, + x2: self.right() as u16, + y2: self.bottom() as u16, + horizontal_dpi: 320, + vertical_dpi: 200, + ega_palette: [0u8; 48], + reserved: 0, + num_color_planes: 1, + bytes_per_line: self.width() as u16, + palette_type: 1, + horizontal_size: self.width() as u16, + vertical_size: self.height() as u16, + padding: [0u8; 54], + }; + header.write(writer)?; - let pixels = self.pixels(); - let mut i = 0; + let pixels = self.pixels(); + let mut i = 0; - for _y in 0..=self.bottom() { - // write one scanline at a time. breaking runs that could have continued across - // scanlines in the process, as per the pcx standard + for _y in 0..=self.bottom() { + // write one scanline at a time. breaking runs that could have continued across + // scanlines in the process, as per the pcx standard - let mut run_count = 0; - let mut run_pixel = 0; + let mut run_count = 0; + let mut run_pixel = 0; - for _x in 0..=self.right() { - let pixel = pixels[i]; - i += 1; + for _x in 0..=self.right() { + let pixel = pixels[i]; + i += 1; - if run_count == 0 { - run_count = 1; - run_pixel = pixel; - } else { - if (pixel != run_pixel) || (run_count >= 63) { - write_pcx_data(writer, run_count, run_pixel)?; - run_count = 1; - run_pixel = pixel; - } else { - run_count += 1; - } - } - } + if run_count == 0 { + run_count = 1; + run_pixel = pixel; + } else { + if (pixel != run_pixel) || (run_count >= 63) { + write_pcx_data(writer, run_count, run_pixel)?; + run_count = 1; + run_pixel = pixel; + } else { + run_count += 1; + } + } + } - // end the scanline, writing out whatever run we might have had going - write_pcx_data(writer, run_count, run_pixel)?; - } + // end the scanline, writing out whatever run we might have had going + write_pcx_data(writer, run_count, run_pixel)?; + } - // marker for beginning of palette data - writer.write_u8(0xc)?; + // marker for beginning of palette data + writer.write_u8(0xc)?; - for i in 0..=255 { - let argb = palette[i]; - let (r, g, b) = from_rgb32(argb); - writer.write_u8(r)?; - writer.write_u8(g)?; - writer.write_u8(b)?; - } + for i in 0..=255 { + let argb = palette[i]; + let (r, g, b) = from_rgb32(argb); + writer.write_u8(r)?; + writer.write_u8(g)?; + writer.write_u8(b)?; + } - Ok(()) - } + Ok(()) + } - pub fn to_pcx_file(&self, path: &Path, palette: &Palette) -> Result<(), PcxError> { - let f = File::create(path)?; - let mut writer = BufWriter::new(f); - self.to_pcx_bytes(&mut writer, palette) - } + pub fn to_pcx_file(&self, path: &Path, palette: &Palette) -> Result<(), PcxError> { + let f = File::create(path)?; + let mut writer = BufWriter::new(f); + self.to_pcx_bytes(&mut writer, palette) + } } #[cfg(test)] pub mod tests { - use tempfile::TempDir; + use tempfile::TempDir; - use super::*; + use super::*; - pub static TEST_BMP_PIXELS_RAW: &[u8] = include_bytes!("../../../test-assets/test_bmp_pixels_raw.bin"); - pub static TEST_LARGE_BMP_PIXELS_RAW: &[u8] = include_bytes!("../../../test-assets/test_large_bmp_pixels_raw.bin"); - pub static TEST_LARGE_BMP_PIXELS_RAW_2: &[u8] = include_bytes!("../../../test-assets/test_large_bmp_pixels_raw2.bin"); + pub static TEST_BMP_PIXELS_RAW: &[u8] = include_bytes!("../../../test-assets/test_bmp_pixels_raw.bin"); + pub static TEST_LARGE_BMP_PIXELS_RAW: &[u8] = include_bytes!("../../../test-assets/test_large_bmp_pixels_raw.bin"); + pub static TEST_LARGE_BMP_PIXELS_RAW_2: &[u8] = include_bytes!("../../../test-assets/test_large_bmp_pixels_raw2.bin"); - #[test] - pub fn load_and_save() -> Result<(), PcxError> { - let dp2_palette = - Palette::load_from_file(Path::new("./test-assets/dp2.pal"), PaletteFormat::Normal) - .unwrap(); - let tmp_dir = TempDir::new()?; + #[test] + pub fn load_and_save() -> Result<(), PcxError> { + let dp2_palette = + Palette::load_from_file(Path::new("./test-assets/dp2.pal"), PaletteFormat::Normal) + .unwrap(); + let tmp_dir = TempDir::new()?; - let (bmp, palette) = Bitmap::load_pcx_file(Path::new("./test-assets/test.pcx"))?; - assert_eq!(16, bmp.width()); - assert_eq!(16, bmp.height()); - assert_eq!(bmp.pixels(), TEST_BMP_PIXELS_RAW); - assert_eq!(palette, dp2_palette); + let (bmp, palette) = Bitmap::load_pcx_file(Path::new("./test-assets/test.pcx"))?; + assert_eq!(16, bmp.width()); + assert_eq!(16, bmp.height()); + assert_eq!(bmp.pixels(), TEST_BMP_PIXELS_RAW); + assert_eq!(palette, dp2_palette); - let save_path = tmp_dir.path().join("test_save.pcx"); - bmp.to_pcx_file(&save_path, &palette)?; - let (reloaded_bmp, reloaded_palette) = Bitmap::load_pcx_file(&save_path)?; - assert_eq!(16, reloaded_bmp.width()); - assert_eq!(16, reloaded_bmp.height()); - assert_eq!(reloaded_bmp.pixels(), TEST_BMP_PIXELS_RAW); - assert_eq!(reloaded_palette, dp2_palette); + let save_path = tmp_dir.path().join("test_save.pcx"); + bmp.to_pcx_file(&save_path, &palette)?; + let (reloaded_bmp, reloaded_palette) = Bitmap::load_pcx_file(&save_path)?; + assert_eq!(16, reloaded_bmp.width()); + assert_eq!(16, reloaded_bmp.height()); + assert_eq!(reloaded_bmp.pixels(), TEST_BMP_PIXELS_RAW); + assert_eq!(reloaded_palette, dp2_palette); - Ok(()) - } + Ok(()) + } - #[test] - pub fn load_and_save_larger_image() -> Result<(), PcxError> { - let tmp_dir = TempDir::new()?; + #[test] + pub fn load_and_save_larger_image() -> Result<(), PcxError> { + let tmp_dir = TempDir::new()?; - // first image + // first image - let (bmp, palette) = Bitmap::load_pcx_file(Path::new("./test-assets/test_image.pcx"))?; - assert_eq!(320, bmp.width()); - assert_eq!(200, bmp.height()); - assert_eq!(bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW); + let (bmp, palette) = Bitmap::load_pcx_file(Path::new("./test-assets/test_image.pcx"))?; + assert_eq!(320, bmp.width()); + assert_eq!(200, bmp.height()); + assert_eq!(bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW); - let save_path = tmp_dir.path().join("test_save.pcx"); - bmp.to_pcx_file(&save_path, &palette)?; - let (reloaded_bmp, _) = Bitmap::load_pcx_file(&save_path)?; - assert_eq!(320, reloaded_bmp.width()); - assert_eq!(200, reloaded_bmp.height()); - assert_eq!(reloaded_bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW); + let save_path = tmp_dir.path().join("test_save.pcx"); + bmp.to_pcx_file(&save_path, &palette)?; + let (reloaded_bmp, _) = Bitmap::load_pcx_file(&save_path)?; + assert_eq!(320, reloaded_bmp.width()); + assert_eq!(200, reloaded_bmp.height()); + assert_eq!(reloaded_bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW); - // second image + // second image - let (bmp, palette) = Bitmap::load_pcx_file(Path::new("./test-assets/test_image2.pcx"))?; - assert_eq!(320, bmp.width()); - assert_eq!(200, bmp.height()); - assert_eq!(bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW_2); + let (bmp, palette) = Bitmap::load_pcx_file(Path::new("./test-assets/test_image2.pcx"))?; + assert_eq!(320, bmp.width()); + assert_eq!(200, bmp.height()); + assert_eq!(bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW_2); - let save_path = tmp_dir.path().join("test_save_2.pcx"); - bmp.to_pcx_file(&save_path, &palette)?; - let (reloaded_bmp, _) = Bitmap::load_pcx_file(&save_path)?; - assert_eq!(320, reloaded_bmp.width()); - assert_eq!(200, reloaded_bmp.height()); - assert_eq!(reloaded_bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW_2); + let save_path = tmp_dir.path().join("test_save_2.pcx"); + bmp.to_pcx_file(&save_path, &palette)?; + let (reloaded_bmp, _) = Bitmap::load_pcx_file(&save_path)?; + assert_eq!(320, reloaded_bmp.width()); + assert_eq!(200, reloaded_bmp.height()); + assert_eq!(reloaded_bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW_2); - Ok(()) - } + Ok(()) + } } diff --git a/libretrogd/src/graphics/bitmap/primitives.rs b/libretrogd/src/graphics/bitmap/primitives.rs index 0a8f00a..5c9406a 100644 --- a/libretrogd/src/graphics/bitmap/primitives.rs +++ b/libretrogd/src/graphics/bitmap/primitives.rs @@ -4,612 +4,612 @@ use crate::graphics::*; use crate::math::*; impl Bitmap { - /// Fills the entire bitmap with the given color. - pub fn clear(&mut self, color: u8) { - self.pixels.fill(color); - } + /// Fills the entire bitmap with the given color. + pub fn clear(&mut self, color: u8) { + self.pixels.fill(color); + } - /// Sets the pixel at the given coordinates to the color specified. If the coordinates lie - /// outside of the bitmaps clipping region, no pixels will be changed. - #[inline] - pub fn set_pixel(&mut self, x: i32, y: i32, color: u8) { - if let Some(pixels) = self.pixels_at_mut(x, y) { - pixels[0] = color; - } - } + /// Sets the pixel at the given coordinates to the color specified. If the coordinates lie + /// outside of the bitmaps clipping region, no pixels will be changed. + #[inline] + pub fn set_pixel(&mut self, x: i32, y: i32, color: u8) { + if let Some(pixels) = self.pixels_at_mut(x, y) { + pixels[0] = color; + } + } - /// Sets the pixel at the given coordinates using a blended color via the specified blend map, - /// or using the color specified if the blend map does not include the given color. If the - /// coordinates lie outside of the bitmaps clipping region, no pixels will be changed. - #[inline] - pub fn set_blended_pixel(&mut self, x: i32, y: i32, color: u8, blend_map: &BlendMap) { - if let Some(pixels) = self.pixels_at_mut(x, y) { - let dest_color = pixels[0]; - if let Some(blended_color) = blend_map.blend(color, dest_color) { - pixels[0] = blended_color; - } else { - pixels[0] = color; - } - } - } + /// Sets the pixel at the given coordinates using a blended color via the specified blend map, + /// or using the color specified if the blend map does not include the given color. If the + /// coordinates lie outside of the bitmaps clipping region, no pixels will be changed. + #[inline] + pub fn set_blended_pixel(&mut self, x: i32, y: i32, color: u8, blend_map: &BlendMap) { + if let Some(pixels) = self.pixels_at_mut(x, y) { + let dest_color = pixels[0]; + if let Some(blended_color) = blend_map.blend(color, dest_color) { + pixels[0] = blended_color; + } else { + pixels[0] = color; + } + } + } - /// Sets the pixel at the given coordinates to the color specified. The coordinates are not - /// checked for validity, so it is up to you to ensure they lie within the bounds of the - /// bitmap. - #[inline] - pub unsafe fn set_pixel_unchecked(&mut self, x: i32, y: i32, color: u8) { - let p = self.pixels_at_mut_ptr_unchecked(x, y); - *p = color; - } + /// Sets the pixel at the given coordinates to the color specified. The coordinates are not + /// checked for validity, so it is up to you to ensure they lie within the bounds of the + /// bitmap. + #[inline] + pub unsafe fn set_pixel_unchecked(&mut self, x: i32, y: i32, color: u8) { + let p = self.pixels_at_mut_ptr_unchecked(x, y); + *p = color; + } - /// Sets the pixel at the given coordinates using a blended color via the specified blend map, - /// or using the color specified if the blend map does not include the given color. The - /// coordinates are not checked for validity, so it is up to you to ensure they lie within the - /// bounds of the bitmap. - #[inline] - pub unsafe fn set_blended_pixel_unchecked(&mut self, x: i32, y: i32, color: u8, blend_map: &BlendMap) { - let p = self.pixels_at_mut_ptr_unchecked(x, y); - if let Some(blended_color) = blend_map.blend(color, *p) { - *p = blended_color; - } else { - *p = color; - } - } + /// Sets the pixel at the given coordinates using a blended color via the specified blend map, + /// or using the color specified if the blend map does not include the given color. The + /// coordinates are not checked for validity, so it is up to you to ensure they lie within the + /// bounds of the bitmap. + #[inline] + pub unsafe fn set_blended_pixel_unchecked(&mut self, x: i32, y: i32, color: u8, blend_map: &BlendMap) { + let p = self.pixels_at_mut_ptr_unchecked(x, y); + if let Some(blended_color) = blend_map.blend(color, *p) { + *p = blended_color; + } else { + *p = color; + } + } - /// Gets the pixel at the given coordinates. If the coordinates lie outside of the bitmaps - /// clipping region, None is returned. - #[inline] - pub fn get_pixel(&self, x: i32, y: i32) -> Option { - if let Some(pixels) = self.pixels_at(x, y) { - Some(pixels[0]) - } else { - None - } - } + /// Gets the pixel at the given coordinates. If the coordinates lie outside of the bitmaps + /// clipping region, None is returned. + #[inline] + pub fn get_pixel(&self, x: i32, y: i32) -> Option { + if let Some(pixels) = self.pixels_at(x, y) { + Some(pixels[0]) + } else { + None + } + } - /// Gets the pixel at the given coordinates. The coordinates are not checked for validity, so - /// it is up to you to ensure they lie within the bounds of the bitmap. - #[inline] - pub unsafe fn get_pixel_unchecked(&self, x: i32, y: i32) -> u8 { - *(self.pixels_at_ptr_unchecked(x, y)) - } + /// Gets the pixel at the given coordinates. The coordinates are not checked for validity, so + /// it is up to you to ensure they lie within the bounds of the bitmap. + #[inline] + pub unsafe fn get_pixel_unchecked(&self, x: i32, y: i32) -> u8 { + *(self.pixels_at_ptr_unchecked(x, y)) + } - /// Renders a single character using the font given. - #[inline] - pub fn print_char(&mut self, ch: char, x: i32, y: i32, opts: FontRenderOpts, font: &T) { - font.character(ch) - .draw(self, x, y, opts); - } + /// Renders a single character using the font given. + #[inline] + pub fn print_char(&mut self, ch: char, x: i32, y: i32, opts: FontRenderOpts, font: &T) { + font.character(ch) + .draw(self, x, y, opts); + } - /// Renders the string of text using the font given. - pub fn print_string(&mut self, text: &str, x: i32, y: i32, opts: FontRenderOpts, font: &T) { - let mut current_x = x; - let mut current_y = y; - for ch in text.chars() { - match ch { - ' ' => current_x += font.space_width() as i32, - '\n' => { - current_x = x; - current_y += font.line_height() as i32 - } - '\r' => (), - otherwise => { - self.print_char(otherwise, current_x, current_y, opts, font); - current_x += font.character(otherwise).bounds().width as i32; - } - } - } - } + /// Renders the string of text using the font given. + pub fn print_string(&mut self, text: &str, x: i32, y: i32, opts: FontRenderOpts, font: &T) { + let mut current_x = x; + let mut current_y = y; + for ch in text.chars() { + match ch { + ' ' => current_x += font.space_width() as i32, + '\n' => { + current_x = x; + current_y += font.line_height() as i32 + } + '\r' => (), + otherwise => { + self.print_char(otherwise, current_x, current_y, opts, font); + current_x += font.character(otherwise).bounds().width as i32; + } + } + } + } - /// Draws a line from x1,y1 to x2,y2. - pub fn line(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: u8) { - let mut dx = x1; - let mut dy = y1; - let delta_x = x2 - x1; - let delta_y = y2 - y1; - let delta_x_abs = delta_x.abs(); - let delta_y_abs = delta_y.abs(); - let delta_x_sign = delta_x.signum(); - let delta_y_sign = delta_y.signum(); - let mut x = delta_x_abs / 2; - let mut y = delta_y_abs / 2; - let offset_x_inc = delta_x_sign; - let offset_y_inc = delta_y_sign * self.width as i32; + /// Draws a line from x1,y1 to x2,y2. + pub fn line(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: u8) { + let mut dx = x1; + let mut dy = y1; + let delta_x = x2 - x1; + let delta_y = y2 - y1; + let delta_x_abs = delta_x.abs(); + let delta_y_abs = delta_y.abs(); + let delta_x_sign = delta_x.signum(); + let delta_y_sign = delta_y.signum(); + let mut x = delta_x_abs / 2; + let mut y = delta_y_abs / 2; + let offset_x_inc = delta_x_sign; + let offset_y_inc = delta_y_sign * self.width as i32; - unsafe { - // safety: while we are blindly getting a pointer to this x/y coordinate, we don't - // write to it unless we know the coordinates are in bounds. - // TODO: should be ok ... ? or am i making too many assumptions about memory layout? - let mut dest = self.pixels_at_mut_ptr_unchecked(x1, y1); + unsafe { + // safety: while we are blindly getting a pointer to this x/y coordinate, we don't + // write to it unless we know the coordinates are in bounds. + // TODO: should be ok ... ? or am i making too many assumptions about memory layout? + let mut dest = self.pixels_at_mut_ptr_unchecked(x1, y1); - if self.is_xy_visible(dx, dy) { - *dest = color; - } + if self.is_xy_visible(dx, dy) { + *dest = color; + } - if delta_x_abs >= delta_y_abs { - for _ in 0..delta_x_abs { - y += delta_y_abs; + if delta_x_abs >= delta_y_abs { + for _ in 0..delta_x_abs { + y += delta_y_abs; - if y >= delta_x_abs { - y -= delta_x_abs; - dy += delta_y_sign; - dest = dest.offset(offset_y_inc as isize); - } + if y >= delta_x_abs { + y -= delta_x_abs; + dy += delta_y_sign; + dest = dest.offset(offset_y_inc as isize); + } - dx += delta_x_sign; - dest = dest.offset(offset_x_inc as isize); + dx += delta_x_sign; + dest = dest.offset(offset_x_inc as isize); - if self.is_xy_visible(dx, dy) { - *dest = color; - } - } - } else { - for _ in 0..delta_y_abs { - x += delta_x_abs; + if self.is_xy_visible(dx, dy) { + *dest = color; + } + } + } else { + for _ in 0..delta_y_abs { + x += delta_x_abs; - if x >= delta_y_abs { - x -= delta_y_abs; - dx += delta_x_sign; - dest = dest.offset(offset_x_inc as isize); - } + if x >= delta_y_abs { + x -= delta_y_abs; + dx += delta_x_sign; + dest = dest.offset(offset_x_inc as isize); + } - dy += delta_y_sign; - dest = dest.offset(offset_y_inc as isize); + dy += delta_y_sign; + dest = dest.offset(offset_y_inc as isize); - if self.is_xy_visible(dx, dy) { - *dest = color; - } - } - } - } - } + if self.is_xy_visible(dx, dy) { + *dest = color; + } + } + } + } + } - /// Draws a line from x1,y1 to x2,y2 by blending the drawn pixels using the given blend map, - /// or the color specified if the blend map does not include this color. - pub fn blended_line(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: u8, blend_map: &BlendMap) { - if let Some(blend_mapping) = blend_map.get_mapping(color) { - let mut dx = x1; - let mut dy = y1; - let delta_x = x2 - x1; - let delta_y = y2 - y1; - let delta_x_abs = delta_x.abs(); - let delta_y_abs = delta_y.abs(); - let delta_x_sign = delta_x.signum(); - let delta_y_sign = delta_y.signum(); - let mut x = delta_x_abs / 2; - let mut y = delta_y_abs / 2; - let offset_x_inc = delta_x_sign; - let offset_y_inc = delta_y_sign * self.width as i32; + /// Draws a line from x1,y1 to x2,y2 by blending the drawn pixels using the given blend map, + /// or the color specified if the blend map does not include this color. + pub fn blended_line(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: u8, blend_map: &BlendMap) { + if let Some(blend_mapping) = blend_map.get_mapping(color) { + let mut dx = x1; + let mut dy = y1; + let delta_x = x2 - x1; + let delta_y = y2 - y1; + let delta_x_abs = delta_x.abs(); + let delta_y_abs = delta_y.abs(); + let delta_x_sign = delta_x.signum(); + let delta_y_sign = delta_y.signum(); + let mut x = delta_x_abs / 2; + let mut y = delta_y_abs / 2; + let offset_x_inc = delta_x_sign; + let offset_y_inc = delta_y_sign * self.width as i32; - unsafe { - // safety: while we are blindly getting a pointer to this x/y coordinate, we don't - // write to it unless we know the coordinates are in bounds. - // TODO: should be ok ... ? or am i making too many assumptions about memory layout? - let mut dest = self.pixels_at_mut_ptr_unchecked(x1, y1); + unsafe { + // safety: while we are blindly getting a pointer to this x/y coordinate, we don't + // write to it unless we know the coordinates are in bounds. + // TODO: should be ok ... ? or am i making too many assumptions about memory layout? + let mut dest = self.pixels_at_mut_ptr_unchecked(x1, y1); - if self.is_xy_visible(dx, dy) { - *dest = blend_mapping[*dest as usize]; - } + if self.is_xy_visible(dx, dy) { + *dest = blend_mapping[*dest as usize]; + } - if delta_x_abs >= delta_y_abs { - for _ in 0..delta_x_abs { - y += delta_y_abs; + if delta_x_abs >= delta_y_abs { + for _ in 0..delta_x_abs { + y += delta_y_abs; - if y >= delta_x_abs { - y -= delta_x_abs; - dy += delta_y_sign; - dest = dest.offset(offset_y_inc as isize); - } + if y >= delta_x_abs { + y -= delta_x_abs; + dy += delta_y_sign; + dest = dest.offset(offset_y_inc as isize); + } - dx += delta_x_sign; - dest = dest.offset(offset_x_inc as isize); + dx += delta_x_sign; + dest = dest.offset(offset_x_inc as isize); - if self.is_xy_visible(dx, dy) { - *dest = blend_mapping[*dest as usize]; - } - } - } else { - for _ in 0..delta_y_abs { - x += delta_x_abs; + if self.is_xy_visible(dx, dy) { + *dest = blend_mapping[*dest as usize]; + } + } + } else { + for _ in 0..delta_y_abs { + x += delta_x_abs; - if x >= delta_y_abs { - x -= delta_y_abs; - dx += delta_x_sign; - dest = dest.offset(offset_x_inc as isize); - } + if x >= delta_y_abs { + x -= delta_y_abs; + dx += delta_x_sign; + dest = dest.offset(offset_x_inc as isize); + } - dy += delta_y_sign; - dest = dest.offset(offset_y_inc as isize); + dy += delta_y_sign; + dest = dest.offset(offset_y_inc as isize); - if self.is_xy_visible(dx, dy) { - *dest = blend_mapping[*dest as usize]; - } - } - } - } - } else { - self.line(x1, y1, x2, y2, color); - } - } + if self.is_xy_visible(dx, dy) { + *dest = blend_mapping[*dest as usize]; + } + } + } + } + } else { + self.line(x1, y1, x2, y2, color); + } + } - /// Draws a horizontal line from x1,y to x2,y. - pub fn horiz_line(&mut self, x1: i32, x2: i32, y: i32, color: u8) { - let mut region = Rect::from_coords(x1, y, x2, y); - if region.clamp_to(&self.clip_region) { - unsafe { - let dest = self.pixels_at_mut_ptr_unchecked(region.x, region.y); - dest.write_bytes(color, region.width as usize); - } - } - } + /// Draws a horizontal line from x1,y to x2,y. + pub fn horiz_line(&mut self, x1: i32, x2: i32, y: i32, color: u8) { + let mut region = Rect::from_coords(x1, y, x2, y); + if region.clamp_to(&self.clip_region) { + unsafe { + let dest = self.pixels_at_mut_ptr_unchecked(region.x, region.y); + dest.write_bytes(color, region.width as usize); + } + } + } - /// Draws a horizontal line from x1,y to x2,y by blending the drawn pixels using the given - /// blend map, or the color specified if the blend map does not include this color. - pub fn blended_horiz_line(&mut self, x1: i32, x2: i32, y: i32, color: u8, blend_map: &BlendMap) { - if let Some(blend_mapping) = blend_map.get_mapping(color) { - let mut region = Rect::from_coords(x1, y, x2, y); - if region.clamp_to(&self.clip_region) { - unsafe { - let dest = self.pixels_at_mut_unchecked(region.x, region.y); - for x in 0..region.width as usize { - dest[x] = blend_mapping[dest[x] as usize]; - } - } - } - } else { - self.horiz_line(x1, x2, y, color); - } - } + /// Draws a horizontal line from x1,y to x2,y by blending the drawn pixels using the given + /// blend map, or the color specified if the blend map does not include this color. + pub fn blended_horiz_line(&mut self, x1: i32, x2: i32, y: i32, color: u8, blend_map: &BlendMap) { + if let Some(blend_mapping) = blend_map.get_mapping(color) { + let mut region = Rect::from_coords(x1, y, x2, y); + if region.clamp_to(&self.clip_region) { + unsafe { + let dest = self.pixels_at_mut_unchecked(region.x, region.y); + for x in 0..region.width as usize { + dest[x] = blend_mapping[dest[x] as usize]; + } + } + } + } else { + self.horiz_line(x1, x2, y, color); + } + } - /// Draws a vertical line from x,y1 to x,y2. - pub fn vert_line(&mut self, x: i32, y1: i32, y2: i32, color: u8) { - let mut region = Rect::from_coords(x, y1, x, y2); - if region.clamp_to(&self.clip_region) { - unsafe { - let mut dest = self.pixels_at_mut_ptr_unchecked(region.x, region.y); - for _ in 0..region.height { - *dest = color; - dest = dest.add(self.width as usize); - } - } - } - } + /// Draws a vertical line from x,y1 to x,y2. + pub fn vert_line(&mut self, x: i32, y1: i32, y2: i32, color: u8) { + let mut region = Rect::from_coords(x, y1, x, y2); + if region.clamp_to(&self.clip_region) { + unsafe { + let mut dest = self.pixels_at_mut_ptr_unchecked(region.x, region.y); + for _ in 0..region.height { + *dest = color; + dest = dest.add(self.width as usize); + } + } + } + } - /// Draws a vertical line from x,y1 to x,y2 by blending the drawn pixels using the given blend - /// map, or the color specified if the blend map does not include this color. - pub fn blended_vert_line(&mut self, x: i32, y1: i32, y2: i32, color: u8, blend_map: &BlendMap) { - if let Some(blend_mapping) = blend_map.get_mapping(color) { - let mut region = Rect::from_coords(x, y1, x, y2); - if region.clamp_to(&self.clip_region) { - unsafe { - let mut dest = self.pixels_at_mut_ptr_unchecked(region.x, region.y); - for _ in 0..region.height { - *dest = blend_mapping[*dest as usize]; - dest = dest.add(self.width as usize); - } - } - } - } else { - self.vert_line(x, y1, y2, color); - } - } + /// Draws a vertical line from x,y1 to x,y2 by blending the drawn pixels using the given blend + /// map, or the color specified if the blend map does not include this color. + pub fn blended_vert_line(&mut self, x: i32, y1: i32, y2: i32, color: u8, blend_map: &BlendMap) { + if let Some(blend_mapping) = blend_map.get_mapping(color) { + let mut region = Rect::from_coords(x, y1, x, y2); + if region.clamp_to(&self.clip_region) { + unsafe { + let mut dest = self.pixels_at_mut_ptr_unchecked(region.x, region.y); + for _ in 0..region.height { + *dest = blend_mapping[*dest as usize]; + dest = dest.add(self.width as usize); + } + } + } + } else { + self.vert_line(x, y1, y2, color); + } + } - /// Draws an empty box (rectangle) using the points x1,y1 and x2,y2 to form the box to be - /// drawn, assuming they are specifying the top-left and bottom-right corners respectively. - pub fn rect(&mut self, mut x1: i32, mut y1: i32, mut x2: i32, mut y2: i32, color: u8) { - // note: need to manually do all this instead of just relying on Rect::from_coords (which - // could otherwise figure all this out for us) mainly just because we need the post-swap - // x1,y1,x2,y2 values for post-region-clamping comparison purposes ... - if x2 < x1 { - swap(&mut x1, &mut x2); - } - if y2 < y1 { - swap(&mut y1, &mut y2); - } - let mut region = Rect { - x: x1, - y: y1, - width: (x2 - x1 + 1) as u32, - height: (y2 - y1 + 1) as u32, - }; - if !region.clamp_to(&self.clip_region) { - return; - } + /// Draws an empty box (rectangle) using the points x1,y1 and x2,y2 to form the box to be + /// drawn, assuming they are specifying the top-left and bottom-right corners respectively. + pub fn rect(&mut self, mut x1: i32, mut y1: i32, mut x2: i32, mut y2: i32, color: u8) { + // note: need to manually do all this instead of just relying on Rect::from_coords (which + // could otherwise figure all this out for us) mainly just because we need the post-swap + // x1,y1,x2,y2 values for post-region-clamping comparison purposes ... + if x2 < x1 { + swap(&mut x1, &mut x2); + } + if y2 < y1 { + swap(&mut y1, &mut y2); + } + let mut region = Rect { + x: x1, + y: y1, + width: (x2 - x1 + 1) as u32, + height: (y2 - y1 + 1) as u32, + }; + if !region.clamp_to(&self.clip_region) { + return; + } - // top line, only if y1 was originally within bounds - if y1 == region.y { - unsafe { - let dest = self.pixels_at_mut_ptr_unchecked(region.x, region.y); - dest.write_bytes(color, region.width as usize); - } - } + // top line, only if y1 was originally within bounds + if y1 == region.y { + unsafe { + let dest = self.pixels_at_mut_ptr_unchecked(region.x, region.y); + dest.write_bytes(color, region.width as usize); + } + } - // bottom line, only if y2 was originally within bounds - if y2 == region.bottom() { - unsafe { - let dest = self.pixels_at_mut_ptr_unchecked(region.x, region.bottom()); - dest.write_bytes(color, region.width as usize); - } - } + // bottom line, only if y2 was originally within bounds + if y2 == region.bottom() { + unsafe { + let dest = self.pixels_at_mut_ptr_unchecked(region.x, region.bottom()); + dest.write_bytes(color, region.width as usize); + } + } - // left line, only if x1 was originally within bounds - if x1 == region.x { - unsafe { - let mut dest = self.pixels_at_mut_ptr_unchecked(region.x, region.y); - for _ in 0..region.height { - *dest = color; - dest = dest.add(self.width as usize); - } - } - } + // left line, only if x1 was originally within bounds + if x1 == region.x { + unsafe { + let mut dest = self.pixels_at_mut_ptr_unchecked(region.x, region.y); + for _ in 0..region.height { + *dest = color; + dest = dest.add(self.width as usize); + } + } + } - // right line, only if x2 was originally within bounds - if x2 == region.right() { - unsafe { - let mut dest = self.pixels_at_mut_ptr_unchecked(region.right(), region.y); - for _ in 0..region.height { - *dest = color; - dest = dest.add(self.width as usize); - } - } - } - } + // right line, only if x2 was originally within bounds + if x2 == region.right() { + unsafe { + let mut dest = self.pixels_at_mut_ptr_unchecked(region.right(), region.y); + for _ in 0..region.height { + *dest = color; + dest = dest.add(self.width as usize); + } + } + } + } - /// Draws an empty box (rectangle) using the points x1,y1 and x2,y2 to form the box to be - /// drawn, assuming they are specifying the top-left and bottom-right corners respectively. - /// The box is drawn by blending the drawn pixels using the given blend map, or the color - /// specified if the blend map does not include this color. - pub fn blended_rect(&mut self, mut x1: i32, mut y1: i32, mut x2: i32, mut y2: i32, color: u8, blend_map: &BlendMap) { - if let Some(blend_mapping) = blend_map.get_mapping(color) { - // note: need to manually do all this instead of just relying on Rect::from_coords (which - // could otherwise figure all this out for us) mainly just because we need the post-swap - // x1,y1,x2,y2 values for post-region-clamping comparison purposes ... - if x2 < x1 { - swap(&mut x1, &mut x2); - } - if y2 < y1 { - swap(&mut y1, &mut y2); - } - let mut region = Rect { - x: x1, - y: y1, - width: (x2 - x1 + 1) as u32, - height: (y2 - y1 + 1) as u32, - }; - if !region.clamp_to(&self.clip_region) { - return; - } + /// Draws an empty box (rectangle) using the points x1,y1 and x2,y2 to form the box to be + /// drawn, assuming they are specifying the top-left and bottom-right corners respectively. + /// The box is drawn by blending the drawn pixels using the given blend map, or the color + /// specified if the blend map does not include this color. + pub fn blended_rect(&mut self, mut x1: i32, mut y1: i32, mut x2: i32, mut y2: i32, color: u8, blend_map: &BlendMap) { + if let Some(blend_mapping) = blend_map.get_mapping(color) { + // note: need to manually do all this instead of just relying on Rect::from_coords (which + // could otherwise figure all this out for us) mainly just because we need the post-swap + // x1,y1,x2,y2 values for post-region-clamping comparison purposes ... + if x2 < x1 { + swap(&mut x1, &mut x2); + } + if y2 < y1 { + swap(&mut y1, &mut y2); + } + let mut region = Rect { + x: x1, + y: y1, + width: (x2 - x1 + 1) as u32, + height: (y2 - y1 + 1) as u32, + }; + if !region.clamp_to(&self.clip_region) { + return; + } - // note that since we're performing a blend based on the existing destination pixel, - // we need to make sure that we don't draw any overlapping corner pixels (where we - // would end up blending with the edge of a previously drawn line). - // to solve this issue, we just cut off the left-most and right-most pixels for the - // two horizontal lines drawn. those corner pixels will be drawn during the vertical - // line drawing instead. + // note that since we're performing a blend based on the existing destination pixel, + // we need to make sure that we don't draw any overlapping corner pixels (where we + // would end up blending with the edge of a previously drawn line). + // to solve this issue, we just cut off the left-most and right-most pixels for the + // two horizontal lines drawn. those corner pixels will be drawn during the vertical + // line drawing instead. - // top line, only if y1 was originally within bounds - if y1 == region.y { - unsafe { - let dest = self.pixels_at_mut_unchecked(region.x, region.y); - for x in 1..(region.width - 1) as usize { - dest[x] = blend_mapping[dest[x] as usize]; - } - } - } + // top line, only if y1 was originally within bounds + if y1 == region.y { + unsafe { + let dest = self.pixels_at_mut_unchecked(region.x, region.y); + for x in 1..(region.width - 1) as usize { + dest[x] = blend_mapping[dest[x] as usize]; + } + } + } - // bottom line, only if y2 was originally within bounds - if y2 == region.bottom() { - unsafe { - let dest = self.pixels_at_mut_unchecked(region.x, region.bottom()); - for x in 1..(region.width - 1) as usize { - dest[x] = blend_mapping[dest[x] as usize]; - } - } - } + // bottom line, only if y2 was originally within bounds + if y2 == region.bottom() { + unsafe { + let dest = self.pixels_at_mut_unchecked(region.x, region.bottom()); + for x in 1..(region.width - 1) as usize { + dest[x] = blend_mapping[dest[x] as usize]; + } + } + } - // left line, only if x1 was originally within bounds - if x1 == region.x { - unsafe { - let mut dest = self.pixels_at_mut_ptr_unchecked(region.x, region.y); - for _ in 0..region.height { - *dest = blend_mapping[*dest as usize]; - dest = dest.add(self.width as usize); - } - } - } + // left line, only if x1 was originally within bounds + if x1 == region.x { + unsafe { + let mut dest = self.pixels_at_mut_ptr_unchecked(region.x, region.y); + for _ in 0..region.height { + *dest = blend_mapping[*dest as usize]; + dest = dest.add(self.width as usize); + } + } + } - // right line, only if x2 was originally within bounds - if x2 == region.right() { - unsafe { - let mut dest = self.pixels_at_mut_ptr_unchecked(region.right(), region.y); - for _ in 0..region.height { - *dest = blend_mapping[*dest as usize]; - dest = dest.add(self.width as usize); - } - } - } - } else { - self.rect(x1, y1, x2, y2, color); - } - } + // right line, only if x2 was originally within bounds + if x2 == region.right() { + unsafe { + let mut dest = self.pixels_at_mut_ptr_unchecked(region.right(), region.y); + for _ in 0..region.height { + *dest = blend_mapping[*dest as usize]; + dest = dest.add(self.width as usize); + } + } + } + } else { + self.rect(x1, y1, x2, y2, color); + } + } - /// Draws a filled box (rectangle) using the points x1,y1 and x2,y2 to form the box to be - /// drawn, assuming they are specifying the top-left and bottom-right corners respectively. - pub fn filled_rect(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: u8) { - let mut region = Rect::from_coords(x1, y1, x2, y2); - if region.clamp_to(&self.clip_region) { - unsafe { - let mut dest = self.pixels_at_mut_ptr_unchecked(region.x, region.y); - for _ in 0..region.height { - dest.write_bytes(color, region.width as usize); - dest = dest.add(self.width as usize); - } - } - } - } + /// Draws a filled box (rectangle) using the points x1,y1 and x2,y2 to form the box to be + /// drawn, assuming they are specifying the top-left and bottom-right corners respectively. + pub fn filled_rect(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: u8) { + let mut region = Rect::from_coords(x1, y1, x2, y2); + if region.clamp_to(&self.clip_region) { + unsafe { + let mut dest = self.pixels_at_mut_ptr_unchecked(region.x, region.y); + for _ in 0..region.height { + dest.write_bytes(color, region.width as usize); + dest = dest.add(self.width as usize); + } + } + } + } - /// Draws a filled box (rectangle) using the points x1,y1 and x2,y2 to form the box to be - /// drawn, assuming they are specifying the top-left and bottom-right corners respectively. The - /// filled box is draw by blending the drawn pixels using the given blend map, or the color - /// specified if the blend map does not include this color. - pub fn blended_filled_rect(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: u8, blend_map: &BlendMap) { - if let Some(blend_mapping) = blend_map.get_mapping(color) { - let mut region = Rect::from_coords(x1, y1, x2, y2); - if region.clamp_to(&self.clip_region) { - unsafe { - let mut dest = self.pixels_at_mut_ptr_unchecked(region.x, region.y); - for _ in 0..region.height { - for x in 0..region.width as usize { - let dest_x = dest.offset(x as isize); - *dest_x = blend_mapping[*dest_x as usize]; - } - dest = dest.add(self.width as usize); - } - } - } - } else { - self.filled_rect(x1, y1, x2, y2, color); - } - } + /// Draws a filled box (rectangle) using the points x1,y1 and x2,y2 to form the box to be + /// drawn, assuming they are specifying the top-left and bottom-right corners respectively. The + /// filled box is draw by blending the drawn pixels using the given blend map, or the color + /// specified if the blend map does not include this color. + pub fn blended_filled_rect(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: u8, blend_map: &BlendMap) { + if let Some(blend_mapping) = blend_map.get_mapping(color) { + let mut region = Rect::from_coords(x1, y1, x2, y2); + if region.clamp_to(&self.clip_region) { + unsafe { + let mut dest = self.pixels_at_mut_ptr_unchecked(region.x, region.y); + for _ in 0..region.height { + for x in 0..region.width as usize { + let dest_x = dest.offset(x as isize); + *dest_x = blend_mapping[*dest_x as usize]; + } + dest = dest.add(self.width as usize); + } + } + } + } else { + self.filled_rect(x1, y1, x2, y2, color); + } + } - /// Draws the outline of a circle formed by the center point and radius given. - pub fn circle(&mut self, center_x: i32, center_y: i32, radius: u32, color: u8) { - // TODO: optimize - let mut x = 0; - let mut y = radius as i32; - let mut m = 5 - 4 * radius as i32; + /// Draws the outline of a circle formed by the center point and radius given. + pub fn circle(&mut self, center_x: i32, center_y: i32, radius: u32, color: u8) { + // TODO: optimize + let mut x = 0; + let mut y = radius as i32; + let mut m = 5 - 4 * radius as i32; - while x <= y { - self.set_pixel(center_x + x, center_y + y, color); - self.set_pixel(center_x + x, center_y - y, color); - self.set_pixel(center_x - x, center_y + y, color); - self.set_pixel(center_x - x, center_y - y, color); - self.set_pixel(center_x + y, center_y + x, color); - self.set_pixel(center_x + y, center_y - x, color); - self.set_pixel(center_x - y, center_y + x, color); - self.set_pixel(center_x - y, center_y - x, color); + while x <= y { + self.set_pixel(center_x + x, center_y + y, color); + self.set_pixel(center_x + x, center_y - y, color); + self.set_pixel(center_x - x, center_y + y, color); + self.set_pixel(center_x - x, center_y - y, color); + self.set_pixel(center_x + y, center_y + x, color); + self.set_pixel(center_x + y, center_y - x, color); + self.set_pixel(center_x - y, center_y + x, color); + self.set_pixel(center_x - y, center_y - x, color); - if m > 0 { - y -= 1; - m -= 8 * y; - } + if m > 0 { + y -= 1; + m -= 8 * y; + } - x += 1; - m += 8 * x + 4; - } - } + x += 1; + m += 8 * x + 4; + } + } - /// Draws a filled circle formed by the center point and radius given. - pub fn filled_circle(&mut self, center_x: i32, center_y: i32, radius: u32, color: u8) { - // TODO: optimize - let mut x = 0; - let mut y = radius as i32; - let mut m = 5 - 4 * radius as i32; + /// Draws a filled circle formed by the center point and radius given. + pub fn filled_circle(&mut self, center_x: i32, center_y: i32, radius: u32, color: u8) { + // TODO: optimize + let mut x = 0; + let mut y = radius as i32; + let mut m = 5 - 4 * radius as i32; - while x <= y { - self.horiz_line(center_x - x, center_x + x, center_y - y, color); - self.horiz_line(center_x - y, center_x + y, center_y - x, color); - self.horiz_line(center_x - y, center_x + y, center_y + x, color); - self.horiz_line(center_x - x, center_x + x, center_y + y, color); + while x <= y { + self.horiz_line(center_x - x, center_x + x, center_y - y, color); + self.horiz_line(center_x - y, center_x + y, center_y - x, color); + self.horiz_line(center_x - y, center_x + y, center_y + x, color); + self.horiz_line(center_x - x, center_x + x, center_y + y, color); - if m > 0 { - y -= 1; - m -= 8 * y; - } + if m > 0 { + y -= 1; + m -= 8 * y; + } - x += 1; - m += 8 * x + 4; - } - } + x += 1; + m += 8 * x + 4; + } + } } #[cfg(test)] pub mod tests { - use super::*; + use super::*; - #[rustfmt::skip] - #[test] - pub fn set_and_get_pixel() { - let mut bmp = Bitmap::new(8, 8).unwrap(); + #[rustfmt::skip] + #[test] + pub fn set_and_get_pixel() { + let mut bmp = Bitmap::new(8, 8).unwrap(); - assert_eq!(None, bmp.get_pixel(-1, -1)); + assert_eq!(None, bmp.get_pixel(-1, -1)); - assert_eq!(0, bmp.get_pixel(0, 0).unwrap()); - bmp.set_pixel(0, 0, 7); - assert_eq!(7, bmp.get_pixel(0, 0).unwrap()); + assert_eq!(0, bmp.get_pixel(0, 0).unwrap()); + bmp.set_pixel(0, 0, 7); + assert_eq!(7, bmp.get_pixel(0, 0).unwrap()); - assert_eq!( - bmp.pixels(), - &[ - 7, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - ] - ); + assert_eq!( + bmp.pixels(), + &[ + 7, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + ] + ); - assert_eq!(0, bmp.get_pixel(2, 4).unwrap()); - bmp.set_pixel(2, 4, 5); - assert_eq!(5, bmp.get_pixel(2, 4).unwrap()); + assert_eq!(0, bmp.get_pixel(2, 4).unwrap()); + bmp.set_pixel(2, 4, 5); + assert_eq!(5, bmp.get_pixel(2, 4).unwrap()); - assert_eq!( - bmp.pixels(), - &[ - 7, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 5, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - ] - ); - } + assert_eq!( + bmp.pixels(), + &[ + 7, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + ] + ); + } - #[rustfmt::skip] - #[test] - pub fn set_and_get_pixel_unchecked() { - let mut bmp = Bitmap::new(8, 8).unwrap(); + #[rustfmt::skip] + #[test] + pub fn set_and_get_pixel_unchecked() { + let mut bmp = Bitmap::new(8, 8).unwrap(); - assert_eq!(0, unsafe { bmp.get_pixel_unchecked(0, 0) }); - unsafe { bmp.set_pixel_unchecked(0, 0, 7) }; - assert_eq!(7, unsafe { bmp.get_pixel_unchecked(0, 0) }); + assert_eq!(0, unsafe { bmp.get_pixel_unchecked(0, 0) }); + unsafe { bmp.set_pixel_unchecked(0, 0, 7) }; + assert_eq!(7, unsafe { bmp.get_pixel_unchecked(0, 0) }); - assert_eq!( - bmp.pixels(), - &[ - 7, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - ] - ); + assert_eq!( + bmp.pixels(), + &[ + 7, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + ] + ); - assert_eq!(0, unsafe { bmp.get_pixel_unchecked(2, 4) }); - unsafe { bmp.set_pixel_unchecked(2, 4, 5) }; - assert_eq!(5, unsafe { bmp.get_pixel_unchecked(2, 4) }); + assert_eq!(0, unsafe { bmp.get_pixel_unchecked(2, 4) }); + unsafe { bmp.set_pixel_unchecked(2, 4, 5) }; + assert_eq!(5, unsafe { bmp.get_pixel_unchecked(2, 4) }); - assert_eq!( - bmp.pixels(), - &[ - 7, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 5, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - ] - ); - } + assert_eq!( + bmp.pixels(), + &[ + 7, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + ] + ); + } } diff --git a/libretrogd/src/graphics/bitmapatlas.rs b/libretrogd/src/graphics/bitmapatlas.rs index 32466a6..f2eecc1 100644 --- a/libretrogd/src/graphics/bitmapatlas.rs +++ b/libretrogd/src/graphics/bitmapatlas.rs @@ -7,181 +7,181 @@ use crate::math::*; #[derive(Error, Debug)] pub enum BitmapAtlasError { - #[error("Region is out of bounds for the Bitmap used by the BitmapAtlas")] - OutOfBounds, + #[error("Region is out of bounds for the Bitmap used by the BitmapAtlas")] + OutOfBounds, } #[derive(Debug, Clone, Eq, PartialEq)] pub struct BitmapAtlas { - bitmap: Bitmap, - bounds: Rect, - tiles: Vec, + bitmap: Bitmap, + bounds: Rect, + tiles: Vec, } impl BitmapAtlas { - pub fn new(bitmap: Bitmap) -> BitmapAtlas { - let bounds = bitmap.full_bounds(); - BitmapAtlas { - bitmap, - bounds, - tiles: Vec::new(), - } - } + pub fn new(bitmap: Bitmap) -> BitmapAtlas { + let bounds = bitmap.full_bounds(); + BitmapAtlas { + bitmap, + bounds, + tiles: Vec::new(), + } + } - pub fn add(&mut self, rect: Rect) -> Result { - if !self.bounds.contains_rect(&rect) { - return Err(BitmapAtlasError::OutOfBounds); - } + pub fn add(&mut self, rect: Rect) -> Result { + if !self.bounds.contains_rect(&rect) { + return Err(BitmapAtlasError::OutOfBounds); + } - self.tiles.push(rect); - Ok(self.tiles.len() - 1) - } + self.tiles.push(rect); + Ok(self.tiles.len() - 1) + } - pub fn add_grid( - &mut self, - tile_width: u32, - tile_height: u32, - ) -> Result { - if self.bounds.width < tile_width || self.bounds.height < tile_height { - return Err(BitmapAtlasError::OutOfBounds); - } + pub fn add_grid( + &mut self, + tile_width: u32, + tile_height: u32, + ) -> Result { + if self.bounds.width < tile_width || self.bounds.height < tile_height { + return Err(BitmapAtlasError::OutOfBounds); + } - for yt in 0..(self.bounds.height / tile_height) { - for xt in 0..(self.bounds.width) / tile_width { - let x = xt * tile_width; - let y = yt * tile_height; - let rect = Rect::new(x as i32, y as i32, tile_width, tile_height); - self.tiles.push(rect); - } - } + for yt in 0..(self.bounds.height / tile_height) { + for xt in 0..(self.bounds.width) / tile_width { + let x = xt * tile_width; + let y = yt * tile_height; + let rect = Rect::new(x as i32, y as i32, tile_width, tile_height); + self.tiles.push(rect); + } + } - Ok(self.tiles.len() - 1) - } + Ok(self.tiles.len() - 1) + } - pub fn add_custom_grid( - &mut self, - start_x: u32, - start_y: u32, - tile_width: u32, - tile_height: u32, - x_tiles: u32, - y_tiles: u32, - border: u32, - ) -> Result { - // figure out of the grid properties given would result in us creating any - // rects that lie out of the bounds of this bitmap - let grid_region = Rect::new( - start_x as i32, - start_y as i32, - (tile_width + border) * x_tiles + border, - (tile_height + border) * y_tiles + border, - ); - if !self.bounds.contains_rect(&grid_region) { - return Err(BitmapAtlasError::OutOfBounds); - } + pub fn add_custom_grid( + &mut self, + start_x: u32, + start_y: u32, + tile_width: u32, + tile_height: u32, + x_tiles: u32, + y_tiles: u32, + border: u32, + ) -> Result { + // figure out of the grid properties given would result in us creating any + // rects that lie out of the bounds of this bitmap + let grid_region = Rect::new( + start_x as i32, + start_y as i32, + (tile_width + border) * x_tiles + border, + (tile_height + border) * y_tiles + border, + ); + if !self.bounds.contains_rect(&grid_region) { + return Err(BitmapAtlasError::OutOfBounds); + } - // all good! now create all the tiles needed for the grid specified - for yt in 0..y_tiles { - for xt in 0..x_tiles { - let x = start_x + (tile_width + border) * xt; - let y = start_y + (tile_height + border) * yt; - let rect = Rect::new(x as i32, y as i32, tile_width, tile_height); - self.tiles.push(rect); - } - } + // all good! now create all the tiles needed for the grid specified + for yt in 0..y_tiles { + for xt in 0..x_tiles { + let x = start_x + (tile_width + border) * xt; + let y = start_y + (tile_height + border) * yt; + let rect = Rect::new(x as i32, y as i32, tile_width, tile_height); + self.tiles.push(rect); + } + } - Ok(self.tiles.len() - 1) - } - - pub fn clear(&mut self) { - self.tiles.clear() - } + Ok(self.tiles.len() - 1) + } - #[inline] - pub fn len(&self) -> usize { - self.tiles.len() - } + pub fn clear(&mut self) { + self.tiles.clear() + } - #[inline] - pub fn get(&self, index: usize) -> Option<&Rect> { - self.tiles.get(index) - } + #[inline] + pub fn len(&self) -> usize { + self.tiles.len() + } - #[inline] - pub fn bitmap(&self) -> &Bitmap { - &self.bitmap - } + #[inline] + pub fn get(&self, index: usize) -> Option<&Rect> { + self.tiles.get(index) + } + + #[inline] + pub fn bitmap(&self) -> &Bitmap { + &self.bitmap + } } impl Index for BitmapAtlas { - type Output = Rect; + type Output = Rect; - #[inline] - fn index(&self, index: usize) -> &Self::Output { - self.get(index).unwrap() - } + #[inline] + fn index(&self, index: usize) -> &Self::Output { + self.get(index).unwrap() + } } #[cfg(test)] pub mod tests { - use claim::*; + use claim::*; - use super::*; + use super::*; - #[test] - pub fn adding_rects() { - let bmp = Bitmap::new(64, 64).unwrap(); - let mut atlas = BitmapAtlas::new(bmp); + #[test] + pub fn adding_rects() { + let bmp = Bitmap::new(64, 64).unwrap(); + let mut atlas = BitmapAtlas::new(bmp); - let rect = Rect::new(0, 0, 16, 16); - assert_eq!(0, atlas.add(rect.clone()).unwrap()); - assert_eq!(rect, atlas[0]); - assert_eq!(1, atlas.len()); + let rect = Rect::new(0, 0, 16, 16); + assert_eq!(0, atlas.add(rect.clone()).unwrap()); + assert_eq!(rect, atlas[0]); + assert_eq!(1, atlas.len()); - let rect = Rect::new(16, 0, 16, 16); - assert_eq!(1, atlas.add(rect.clone()).unwrap()); - assert_eq!(rect, atlas[1]); - assert_eq!(2, atlas.len()); + let rect = Rect::new(16, 0, 16, 16); + assert_eq!(1, atlas.add(rect.clone()).unwrap()); + assert_eq!(rect, atlas[1]); + assert_eq!(2, atlas.len()); - assert_matches!( + assert_matches!( atlas.add(Rect::new(56, 0, 16, 16)), Err(BitmapAtlasError::OutOfBounds) ); - assert_eq!(2, atlas.len()); + assert_eq!(2, atlas.len()); - assert_matches!( + assert_matches!( atlas.add(Rect::new(-8, 4, 16, 16)), Err(BitmapAtlasError::OutOfBounds) ); - assert_eq!(2, atlas.len()); + assert_eq!(2, atlas.len()); - assert_matches!( + assert_matches!( atlas.add(Rect::new(0, 0, 128, 128)), Err(BitmapAtlasError::OutOfBounds) ); - assert_eq!(2, atlas.len()); - } + assert_eq!(2, atlas.len()); + } - #[test] - pub fn adding_grid() { - let bmp = Bitmap::new(64, 64).unwrap(); - let mut atlas = BitmapAtlas::new(bmp); + #[test] + pub fn adding_grid() { + let bmp = Bitmap::new(64, 64).unwrap(); + let mut atlas = BitmapAtlas::new(bmp); - assert_eq!(3, atlas.add_custom_grid(0, 0, 8, 8, 2, 2, 0).unwrap()); - assert_eq!(4, atlas.len()); - assert_eq!(Rect::new(0, 0, 8, 8), atlas[0]); - assert_eq!(Rect::new(8, 0, 8, 8), atlas[1]); - assert_eq!(Rect::new(0, 8, 8, 8), atlas[2]); - assert_eq!(Rect::new(8, 8, 8, 8), atlas[3]); + assert_eq!(3, atlas.add_custom_grid(0, 0, 8, 8, 2, 2, 0).unwrap()); + assert_eq!(4, atlas.len()); + assert_eq!(Rect::new(0, 0, 8, 8), atlas[0]); + assert_eq!(Rect::new(8, 0, 8, 8), atlas[1]); + assert_eq!(Rect::new(0, 8, 8, 8), atlas[2]); + assert_eq!(Rect::new(8, 8, 8, 8), atlas[3]); - atlas.clear(); - assert_eq!(0, atlas.len()); + atlas.clear(); + assert_eq!(0, atlas.len()); - assert_eq!(3, atlas.add_custom_grid(0, 0, 4, 8, 2, 2, 1).unwrap()); - assert_eq!(4, atlas.len()); - assert_eq!(Rect::new(0, 0, 4, 8), atlas[0]); - assert_eq!(Rect::new(5, 0, 4, 8), atlas[1]); - assert_eq!(Rect::new(0, 9, 4, 8), atlas[2]); - assert_eq!(Rect::new(5, 9, 4, 8), atlas[3]); - } + assert_eq!(3, atlas.add_custom_grid(0, 0, 4, 8, 2, 2, 1).unwrap()); + assert_eq!(4, atlas.len()); + assert_eq!(Rect::new(0, 0, 4, 8), atlas[0]); + assert_eq!(Rect::new(5, 0, 4, 8), atlas[1]); + assert_eq!(Rect::new(0, 9, 4, 8), atlas[2]); + assert_eq!(Rect::new(5, 9, 4, 8), atlas[3]); + } } diff --git a/libretrogd/src/graphics/blendmap.rs b/libretrogd/src/graphics/blendmap.rs index 2e8c5ce..25c9776 100644 --- a/libretrogd/src/graphics/blendmap.rs +++ b/libretrogd/src/graphics/blendmap.rs @@ -12,14 +12,14 @@ use crate::utils::bytes::ReadFixedLengthByteArray; #[derive(Error, Debug)] pub enum BlendMapError { - #[error("Source color {0} is out of range for this BlendMap")] - InvalidSourceColor(u8), + #[error("Source color {0} is out of range for this BlendMap")] + InvalidSourceColor(u8), - #[error("Bad or unsupported BlendMap file: {0}")] - BadFile(String), + #[error("Bad or unsupported BlendMap file: {0}")] + BadFile(String), - #[error("BlendMap I/O error")] - IOError(#[from] std::io::Error), + #[error("BlendMap I/O error")] + IOError(#[from] std::io::Error), } /// A lookup table used by [`BlendMap`]s. This table stores destination color to blend color @@ -39,378 +39,378 @@ pub type BlendMapping = [u8; 256]; /// source color, it will have 256 destination to blended color mappings. #[derive(Clone, Eq, PartialEq)] pub struct BlendMap { - start_color: u8, - end_color: u8, - mapping: Box<[BlendMapping]>, + start_color: u8, + end_color: u8, + mapping: Box<[BlendMapping]>, } impl std::fmt::Debug for BlendMap { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("BlendMap") - .field("start_color", &self.start_color) - .field("end_color", &self.end_color) - .finish_non_exhaustive() - } + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("BlendMap") + .field("start_color", &self.start_color) + .field("end_color", &self.end_color) + .finish_non_exhaustive() + } } impl BlendMap { - /// Creates and returns a new [`BlendMap`] with source color mappings for the given inclusive - /// range only. The `start_color` and `end_color` may also be equal to create a blend map with - /// only a single source color mapping. - pub fn new(start_color: u8, end_color: u8) -> Self { - let (start_color, end_color) = if start_color > end_color { - (end_color, start_color) - } else { - (start_color, end_color) - }; - let num_colors = (end_color - start_color) as usize + 1; - BlendMap { - start_color, - end_color, - mapping: vec![[0u8; 256]; num_colors].into_boxed_slice(), - } - } + /// Creates and returns a new [`BlendMap`] with source color mappings for the given inclusive + /// range only. The `start_color` and `end_color` may also be equal to create a blend map with + /// only a single source color mapping. + pub fn new(start_color: u8, end_color: u8) -> Self { + let (start_color, end_color) = if start_color > end_color { + (end_color, start_color) + } else { + (start_color, end_color) + }; + let num_colors = (end_color - start_color) as usize + 1; + BlendMap { + start_color, + end_color, + mapping: vec![[0u8; 256]; num_colors].into_boxed_slice(), + } + } - /// Creates and returns a new [`BlendMap`] with a single source color mapping which maps to - /// a table pre-calculated for the given palette based on the color gradient specified. The - /// resulting blend map can be used to create simple "colorization" overlay effects, which look - /// like a simple translucency effect. The starting color in the gradient is used as the source - /// color mapping in the returned blend map. - pub fn new_colorized_map(gradient_start: u8, gradient_end: u8, palette: &Palette) -> Self { - let (gradient_start, gradient_end) = if gradient_start > gradient_end { - (gradient_end, gradient_start) - } else { - (gradient_start, gradient_end) - }; - let gradient_size = gradient_end - gradient_start + 1; - let source_color = gradient_start; + /// Creates and returns a new [`BlendMap`] with a single source color mapping which maps to + /// a table pre-calculated for the given palette based on the color gradient specified. The + /// resulting blend map can be used to create simple "colorization" overlay effects, which look + /// like a simple translucency effect. The starting color in the gradient is used as the source + /// color mapping in the returned blend map. + pub fn new_colorized_map(gradient_start: u8, gradient_end: u8, palette: &Palette) -> Self { + let (gradient_start, gradient_end) = if gradient_start > gradient_end { + (gradient_end, gradient_start) + } else { + (gradient_start, gradient_end) + }; + let gradient_size = gradient_end - gradient_start + 1; + let source_color = gradient_start; - let mut blend_map = Self::new(source_color, source_color); - for idx in 0..=255 { - let (r, g, b) = from_rgb32(palette[idx]); - let lit = (luminance(r, g, b) * 255.0) as u8; - blend_map.set_mapping( - source_color, - idx as u8, - (gradient_size - 1) - (lit / (256 / gradient_size as u32) as u8) + source_color - ).unwrap(); - } - blend_map - } + let mut blend_map = Self::new(source_color, source_color); + for idx in 0..=255 { + let (r, g, b) = from_rgb32(palette[idx]); + let lit = (luminance(r, g, b) * 255.0) as u8; + blend_map.set_mapping( + source_color, + idx as u8, + (gradient_size - 1) - (lit / (256 / gradient_size as u32) as u8) + source_color, + ).unwrap(); + } + blend_map + } - /// Creates and returns a new [`BlendMap`] which can be used to blend source colors together - /// with the destination using a colorization effect based on a function providing a custom - /// calculation combining the source and destination color luminance values to return a weight - /// into the gradient range given. - pub fn new_colored_luminance_map( - gradient_start: u8, - gradient_end: u8, - palette: &Palette, - f: impl Fn(f32, f32) -> f32 - ) -> BlendMap { - let (gradient_start, gradient_end) = if gradient_start > gradient_end { - (gradient_end, gradient_start) - } else { - (gradient_start, gradient_end) - }; - let gradient_size = gradient_end - gradient_start + 1; + /// Creates and returns a new [`BlendMap`] which can be used to blend source colors together + /// with the destination using a colorization effect based on a function providing a custom + /// calculation combining the source and destination color luminance values to return a weight + /// into the gradient range given. + pub fn new_colored_luminance_map( + gradient_start: u8, + gradient_end: u8, + palette: &Palette, + f: impl Fn(f32, f32) -> f32, + ) -> BlendMap { + let (gradient_start, gradient_end) = if gradient_start > gradient_end { + (gradient_end, gradient_start) + } else { + (gradient_start, gradient_end) + }; + let gradient_size = gradient_end - gradient_start + 1; - let mut blend_map = BlendMap::new(0, 255); - for source_color in 0..=255 { - let (r, g, b) = from_rgb32(palette[source_color]); - let source_luminance = luminance(r, g, b); - for dest_color in 0..=255 { - let (r, g, b) = from_rgb32(palette[dest_color]); - let destination_luminance = luminance(r, g, b); - let weight = (f(source_luminance, destination_luminance) * 255.0) as u8; - blend_map.set_mapping( - source_color, - dest_color, - (gradient_size - 1).wrapping_sub(weight / (256 / gradient_size as u32) as u8) + gradient_start - ).unwrap(); - } - } - blend_map - } + let mut blend_map = BlendMap::new(0, 255); + for source_color in 0..=255 { + let (r, g, b) = from_rgb32(palette[source_color]); + let source_luminance = luminance(r, g, b); + for dest_color in 0..=255 { + let (r, g, b) = from_rgb32(palette[dest_color]); + let destination_luminance = luminance(r, g, b); + let weight = (f(source_luminance, destination_luminance) * 255.0) as u8; + blend_map.set_mapping( + source_color, + dest_color, + (gradient_size - 1).wrapping_sub(weight / (256 / gradient_size as u32) as u8) + gradient_start, + ).unwrap(); + } + } + blend_map + } - /// Creates and returns a new [`BlendMap`] which can be used to blend all 256 colors together - /// with every other color, weighting the blending based on the ratios given where 0.0 will - /// result in that component being totally transparent and 1.0, totally opaque. - /// - /// This method is SLOW! It is computing 65536 different blend colors by searching the given - /// palette for the closest RGB match between two colors. - /// - /// Because simple palette searches are being used to build the blending table, results will - /// vary palette to palette. There will not always be a perfect blend color available. - pub fn new_translucency_map(blend_r: f32, blend_g: f32, blend_b: f32, palette: &Palette) -> Self { - let mut blend_map = BlendMap::new(0, 255); - for source in 0..=255 { - let (source_r, source_g, source_b) = from_rgb32(palette[source]); - let mapping = blend_map.get_mapping_mut(source).unwrap(); - for dest in 0..=255 { - let (dest_r, dest_g, dest_b) = from_rgb32(palette[dest]); + /// Creates and returns a new [`BlendMap`] which can be used to blend all 256 colors together + /// with every other color, weighting the blending based on the ratios given where 0.0 will + /// result in that component being totally transparent and 1.0, totally opaque. + /// + /// This method is SLOW! It is computing 65536 different blend colors by searching the given + /// palette for the closest RGB match between two colors. + /// + /// Because simple palette searches are being used to build the blending table, results will + /// vary palette to palette. There will not always be a perfect blend color available. + pub fn new_translucency_map(blend_r: f32, blend_g: f32, blend_b: f32, palette: &Palette) -> Self { + let mut blend_map = BlendMap::new(0, 255); + for source in 0..=255 { + let (source_r, source_g, source_b) = from_rgb32(palette[source]); + let mapping = blend_map.get_mapping_mut(source).unwrap(); + for dest in 0..=255 { + let (dest_r, dest_g, dest_b) = from_rgb32(palette[dest]); - let find_r = lerp(dest_r as f32, source_r as f32, blend_r) as u8; - let find_g = lerp(dest_g as f32, source_g as f32, blend_g) as u8; - let find_b = lerp(dest_b as f32, source_b as f32, blend_b) as u8; + let find_r = lerp(dest_r as f32, source_r as f32, blend_r) as u8; + let find_g = lerp(dest_g as f32, source_g as f32, blend_g) as u8; + let find_b = lerp(dest_b as f32, source_b as f32, blend_b) as u8; - let result_c = palette.find_color(find_r, find_g, find_b); - mapping[dest as usize] = result_c; - } - } - blend_map - } + let result_c = palette.find_color(find_r, find_g, find_b); + mapping[dest as usize] = result_c; + } + } + blend_map + } - /// The beginning source color that is mapped in this blend map. - #[inline] - pub fn start_color(&self) -> u8 { - self.start_color - } + /// The beginning source color that is mapped in this blend map. + #[inline] + pub fn start_color(&self) -> u8 { + self.start_color + } - /// The ending source color that is mapped in this blend map. - #[inline] - pub fn end_color(&self) -> u8 { - self.end_color - } + /// The ending source color that is mapped in this blend map. + #[inline] + pub fn end_color(&self) -> u8 { + self.end_color + } - /// Returns true if the given source color is mapped in this blend map. - #[inline] - pub fn is_mapped(&self, color: u8) -> bool { - color >= self.start_color && color <= self.end_color - } + /// Returns true if the given source color is mapped in this blend map. + #[inline] + pub fn is_mapped(&self, color: u8) -> bool { + color >= self.start_color && color <= self.end_color + } - #[inline] - fn get_mapping_index(&self, color: u8) -> Option { - if color >= self.start_color && color <= self.end_color { - let index = (color - self.start_color) as usize; - Some(index) - } else { - None - } - } + #[inline] + fn get_mapping_index(&self, color: u8) -> Option { + if color >= self.start_color && color <= self.end_color { + let index = (color - self.start_color) as usize; + Some(index) + } else { + None + } + } - /// Returns a reference to the destination-to-blend color mapping table for the given source - /// color. Returns `None` if the specified source color is not in this blend map. - #[inline] - pub fn get_mapping(&self, color: u8) -> Option<&BlendMapping> { - if let Some(index) = self.get_mapping_index(color) { - // safety: index cannot be outside 0-255 since color and start_color are both u8 - unsafe { Some(self.mapping.get_unchecked(index)) } - } else { - None - } - } + /// Returns a reference to the destination-to-blend color mapping table for the given source + /// color. Returns `None` if the specified source color is not in this blend map. + #[inline] + pub fn get_mapping(&self, color: u8) -> Option<&BlendMapping> { + if let Some(index) = self.get_mapping_index(color) { + // safety: index cannot be outside 0-255 since color and start_color are both u8 + unsafe { Some(self.mapping.get_unchecked(index)) } + } else { + None + } + } - /// Returns a mutable reference to the destination-to-blend color mapping table for the given - /// source color. Returns `None` if the specified source color is not in this blend map. - #[inline] - pub fn get_mapping_mut(&mut self, color: u8) -> Option<&mut BlendMapping> { - if let Some(index) = self.get_mapping_index(color) { - // safety: index cannot be outside 0-255 since color and start_color are both u8 - unsafe { Some(self.mapping.get_unchecked_mut(index)) } - } else { - None - } - } + /// Returns a mutable reference to the destination-to-blend color mapping table for the given + /// source color. Returns `None` if the specified source color is not in this blend map. + #[inline] + pub fn get_mapping_mut(&mut self, color: u8) -> Option<&mut BlendMapping> { + if let Some(index) = self.get_mapping_index(color) { + // safety: index cannot be outside 0-255 since color and start_color are both u8 + unsafe { Some(self.mapping.get_unchecked_mut(index)) } + } else { + None + } + } - /// Sets the blend color mapping for the given source color and destination color combination. - pub fn set_mapping(&mut self, source_color: u8, dest_color: u8, blended_color: u8) -> Result<(), BlendMapError> { - if let Some(mapping) = self.get_mapping_mut(source_color) { - mapping[dest_color as usize] = blended_color; - Ok(()) - } else { - Err(BlendMapError::InvalidSourceColor(source_color)) - } - } + /// Sets the blend color mapping for the given source color and destination color combination. + pub fn set_mapping(&mut self, source_color: u8, dest_color: u8, blended_color: u8) -> Result<(), BlendMapError> { + if let Some(mapping) = self.get_mapping_mut(source_color) { + mapping[dest_color as usize] = blended_color; + Ok(()) + } else { + Err(BlendMapError::InvalidSourceColor(source_color)) + } + } - /// Sets a series of blend color mappings for the given source color and starting from a base - /// destination color. - pub fn set_mappings(&mut self, source_color: u8, base_dest_color: u8, mappings: [u8; N]) -> Result<(), BlendMapError> { - if let Some(mapping) = self.get_mapping_mut(source_color) { - assert!((base_dest_color as usize + N - 1) <= 255, "mappings array is too big for the remaining colors available"); - for index in 0..N { - mapping[index + base_dest_color as usize] = mappings[index]; - } - Ok(()) - } else { - Err(BlendMapError::InvalidSourceColor(source_color)) - } - } + /// Sets a series of blend color mappings for the given source color and starting from a base + /// destination color. + pub fn set_mappings(&mut self, source_color: u8, base_dest_color: u8, mappings: [u8; N]) -> Result<(), BlendMapError> { + if let Some(mapping) = self.get_mapping_mut(source_color) { + assert!((base_dest_color as usize + N - 1) <= 255, "mappings array is too big for the remaining colors available"); + for index in 0..N { + mapping[index + base_dest_color as usize] = mappings[index]; + } + Ok(()) + } else { + Err(BlendMapError::InvalidSourceColor(source_color)) + } + } - /// Returns the blend color for the given source and destination colors. If the source color - /// is not in this blend map, `None` is returned. - #[inline] - pub fn blend(&self, source_color: u8, dest_color: u8) -> Option { - if let Some(mapping) = self.get_mapping(source_color) { - Some(mapping[dest_color as usize]) - } else { - None - } - } + /// Returns the blend color for the given source and destination colors. If the source color + /// is not in this blend map, `None` is returned. + #[inline] + pub fn blend(&self, source_color: u8, dest_color: u8) -> Option { + if let Some(mapping) = self.get_mapping(source_color) { + Some(mapping[dest_color as usize]) + } else { + None + } + } - pub fn load_from_file(path: &Path) -> Result { - let f = File::open(path)?; - let mut reader = BufReader::new(f); - Self::load_from_bytes(&mut reader) - } + pub fn load_from_file(path: &Path) -> Result { + let f = File::open(path)?; + let mut reader = BufReader::new(f); + Self::load_from_bytes(&mut reader) + } - pub fn load_from_bytes(reader: &mut T) -> Result { - let ident: [u8; 4] = reader.read_bytes()?; - if ident != *b"BMap" { - return Err(BlendMapError::BadFile(String::from("Unrecognized header"))); - } + pub fn load_from_bytes(reader: &mut T) -> Result { + let ident: [u8; 4] = reader.read_bytes()?; + if ident != *b"BMap" { + return Err(BlendMapError::BadFile(String::from("Unrecognized header"))); + } - let start_color = reader.read_u8()?; - let end_color = reader.read_u8()?; - let num_maps = end_color as usize - start_color as usize + 1; + let start_color = reader.read_u8()?; + let end_color = reader.read_u8()?; + let num_maps = end_color as usize - start_color as usize + 1; - let mut maps = Vec::with_capacity(num_maps); - for _ in 0..num_maps { - let map: BlendMapping = reader.read_bytes()?; - maps.push(map); - } + let mut maps = Vec::with_capacity(num_maps); + for _ in 0..num_maps { + let map: BlendMapping = reader.read_bytes()?; + maps.push(map); + } - Ok(BlendMap { - start_color, - end_color, - mapping: maps.into_boxed_slice() - }) - } + Ok(BlendMap { + start_color, + end_color, + mapping: maps.into_boxed_slice(), + }) + } - pub fn to_file(&self, path: &Path) -> Result<(), BlendMapError> { - let f = File::create(path)?; - let mut writer = BufWriter::new(f); - self.to_bytes(&mut writer) - } + pub fn to_file(&self, path: &Path) -> Result<(), BlendMapError> { + let f = File::create(path)?; + let mut writer = BufWriter::new(f); + self.to_bytes(&mut writer) + } - pub fn to_bytes(&self, writer: &mut T) -> Result<(), BlendMapError> { - writer.write_all(b"BMap")?; - writer.write_u8(self.start_color)?; - writer.write_u8(self.end_color)?; - for map in self.mapping.iter() { - writer.write_all(map)?; - } - Ok(()) - } + pub fn to_bytes(&self, writer: &mut T) -> Result<(), BlendMapError> { + writer.write_all(b"BMap")?; + writer.write_u8(self.start_color)?; + writer.write_u8(self.end_color)?; + for map in self.mapping.iter() { + writer.write_all(map)?; + } + Ok(()) + } } #[cfg(test)] mod tests { - use claim::*; - use tempfile::TempDir; + use claim::*; + use tempfile::TempDir; - use super::*; + use super::*; - #[test] - pub fn create() -> Result<(), BlendMapError> { - let blend_map = BlendMap::new(10, 12); - assert_eq!(10, blend_map.start_color()); - assert_eq!(12, blend_map.end_color()); - assert!(blend_map.is_mapped(10)); - assert!(blend_map.is_mapped(11)); - assert!(blend_map.is_mapped(12)); - assert!(!blend_map.is_mapped(9)); - assert!(!blend_map.is_mapped(13)); - assert_some!(blend_map.get_mapping(10)); - assert_some!(blend_map.get_mapping(11)); - assert_some!(blend_map.get_mapping(12)); - assert_none!(blend_map.get_mapping(9)); - assert_none!(blend_map.get_mapping(13)); + #[test] + pub fn create() -> Result<(), BlendMapError> { + let blend_map = BlendMap::new(10, 12); + assert_eq!(10, blend_map.start_color()); + assert_eq!(12, blend_map.end_color()); + assert!(blend_map.is_mapped(10)); + assert!(blend_map.is_mapped(11)); + assert!(blend_map.is_mapped(12)); + assert!(!blend_map.is_mapped(9)); + assert!(!blend_map.is_mapped(13)); + assert_some!(blend_map.get_mapping(10)); + assert_some!(blend_map.get_mapping(11)); + assert_some!(blend_map.get_mapping(12)); + assert_none!(blend_map.get_mapping(9)); + assert_none!(blend_map.get_mapping(13)); - let blend_map = BlendMap::new(12, 10); - assert_eq!(10, blend_map.start_color()); - assert_eq!(12, blend_map.end_color()); - assert!(blend_map.is_mapped(10)); - assert!(blend_map.is_mapped(11)); - assert!(blend_map.is_mapped(12)); - assert!(!blend_map.is_mapped(9)); - assert!(!blend_map.is_mapped(13)); - assert_some!(blend_map.get_mapping(10)); - assert_some!(blend_map.get_mapping(11)); - assert_some!(blend_map.get_mapping(12)); - assert_none!(blend_map.get_mapping(9)); - assert_none!(blend_map.get_mapping(13)); + let blend_map = BlendMap::new(12, 10); + assert_eq!(10, blend_map.start_color()); + assert_eq!(12, blend_map.end_color()); + assert!(blend_map.is_mapped(10)); + assert!(blend_map.is_mapped(11)); + assert!(blend_map.is_mapped(12)); + assert!(!blend_map.is_mapped(9)); + assert!(!blend_map.is_mapped(13)); + assert_some!(blend_map.get_mapping(10)); + assert_some!(blend_map.get_mapping(11)); + assert_some!(blend_map.get_mapping(12)); + assert_none!(blend_map.get_mapping(9)); + assert_none!(blend_map.get_mapping(13)); - let blend_map = BlendMap::new(130, 130); - assert_eq!(130, blend_map.start_color()); - assert_eq!(130, blend_map.end_color()); - assert!(blend_map.is_mapped(130)); - assert!(!blend_map.is_mapped(129)); - assert!(!blend_map.is_mapped(131)); - assert_some!(blend_map.get_mapping(130)); - assert_none!(blend_map.get_mapping(129)); - assert_none!(blend_map.get_mapping(131)); + let blend_map = BlendMap::new(130, 130); + assert_eq!(130, blend_map.start_color()); + assert_eq!(130, blend_map.end_color()); + assert!(blend_map.is_mapped(130)); + assert!(!blend_map.is_mapped(129)); + assert!(!blend_map.is_mapped(131)); + assert_some!(blend_map.get_mapping(130)); + assert_none!(blend_map.get_mapping(129)); + assert_none!(blend_map.get_mapping(131)); - Ok(()) - } + Ok(()) + } - #[test] - pub fn mapping() -> Result<(), BlendMapError> { - let mut blend_map = BlendMap::new(16, 31); + #[test] + pub fn mapping() -> Result<(), BlendMapError> { + let mut blend_map = BlendMap::new(16, 31); - assert_none!(blend_map.blend(15, 0)); - assert_eq!(Some(0), blend_map.blend(16, 0)); - assert_eq!(Some(0), blend_map.blend(16, 1)); - assert_ok!(blend_map.set_mapping(16, 0, 116)); - assert_eq!(Some(116), blend_map.blend(16, 0)); - assert_eq!(Some(0), blend_map.blend(16, 1)); + assert_none!(blend_map.blend(15, 0)); + assert_eq!(Some(0), blend_map.blend(16, 0)); + assert_eq!(Some(0), blend_map.blend(16, 1)); + assert_ok!(blend_map.set_mapping(16, 0, 116)); + assert_eq!(Some(116), blend_map.blend(16, 0)); + assert_eq!(Some(0), blend_map.blend(16, 1)); - let mapping = blend_map.get_mapping(16).unwrap(); - assert_eq!(116, mapping[0]); - assert_eq!(0, mapping[1]); + let mapping = blend_map.get_mapping(16).unwrap(); + assert_eq!(116, mapping[0]); + assert_eq!(0, mapping[1]); - assert_eq!(Some(0), blend_map.blend(17, 0)); - assert_ok!(blend_map.set_mapping(17, 0, 117)); - assert_eq!(Some(117), blend_map.blend(17, 0)); - let mapping = blend_map.get_mapping_mut(17).unwrap(); - assert_eq!(117, mapping[0]); - mapping[0] = 217; - assert_eq!(Some(217), blend_map.blend(17, 0)); + assert_eq!(Some(0), blend_map.blend(17, 0)); + assert_ok!(blend_map.set_mapping(17, 0, 117)); + assert_eq!(Some(117), blend_map.blend(17, 0)); + let mapping = blend_map.get_mapping_mut(17).unwrap(); + assert_eq!(117, mapping[0]); + mapping[0] = 217; + assert_eq!(Some(217), blend_map.blend(17, 0)); - assert_matches!( + assert_matches!( blend_map.set_mapping(64, 1, 2), Err(BlendMapError::InvalidSourceColor(64)) ); - Ok(()) - } + Ok(()) + } - #[test] - pub fn bulk_mappings() -> Result<(), BlendMapError> { - let mut blend_map = BlendMap::new(0, 7); + #[test] + pub fn bulk_mappings() -> Result<(), BlendMapError> { + let mut blend_map = BlendMap::new(0, 7); - let mapping = blend_map.get_mapping(2).unwrap(); - assert_eq!([0, 0, 0, 0, 0, 0, 0, 0], mapping[0..8]); + let mapping = blend_map.get_mapping(2).unwrap(); + assert_eq!([0, 0, 0, 0, 0, 0, 0, 0], mapping[0..8]); - assert_ok!(blend_map.set_mappings(2, 4, [1, 2, 3, 4, 5, 6, 7, 8])); + assert_ok!(blend_map.set_mappings(2, 4, [1, 2, 3, 4, 5, 6, 7, 8])); - let mapping = blend_map.get_mapping(2).unwrap(); - assert_eq!( - [0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0], - mapping[0..16] - ); + let mapping = blend_map.get_mapping(2).unwrap(); + assert_eq!( + [0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0], + mapping[0..16] + ); - Ok(()) - } + Ok(()) + } - #[test] - fn load_and_save() -> Result<(), BlendMapError> { - let tmp_dir = TempDir::new()?; + #[test] + fn load_and_save() -> Result<(), BlendMapError> { + let tmp_dir = TempDir::new()?; - let mut blend_map = BlendMap::new(2, 3); - for i in 0..=255 { - blend_map.set_mapping(2, i, i)?; - blend_map.set_mapping(3, i, 255 - i)?; - } + let mut blend_map = BlendMap::new(2, 3); + for i in 0..=255 { + blend_map.set_mapping(2, i, i)?; + blend_map.set_mapping(3, i, 255 - i)?; + } - let save_path = tmp_dir.path().join("test_blend_map.blendmap"); - blend_map.to_file(&save_path)?; + let save_path = tmp_dir.path().join("test_blend_map.blendmap"); + blend_map.to_file(&save_path)?; - let loaded_blend_map = BlendMap::load_from_file(&save_path)?; - assert!(blend_map == loaded_blend_map, "loaded BlendMap is not the same as the original"); + let loaded_blend_map = BlendMap::load_from_file(&save_path)?; + assert!(blend_map == loaded_blend_map, "loaded BlendMap is not the same as the original"); - Ok(()) - } + Ok(()) + } } \ No newline at end of file diff --git a/libretrogd/src/graphics/font.rs b/libretrogd/src/graphics/font.rs index b159775..179c60a 100644 --- a/libretrogd/src/graphics/font.rs +++ b/libretrogd/src/graphics/font.rs @@ -17,263 +17,263 @@ pub const CHAR_FIXED_WIDTH: usize = 8; #[derive(Error, Debug)] pub enum FontError { - #[error("Invalid font file: {0}")] - InvalidFile(String), + #[error("Invalid font file: {0}")] + InvalidFile(String), - #[error("Font I/O error")] - IOError(#[from] std::io::Error), + #[error("Font I/O error")] + IOError(#[from] std::io::Error), } #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum FontRenderOpts { - Color(u8), - None, + Color(u8), + None, } pub trait Character { - fn bounds(&self) -> &Rect; - fn draw(&self, dest: &mut Bitmap, x: i32, y: i32, opts: FontRenderOpts); + fn bounds(&self) -> &Rect; + fn draw(&self, dest: &mut Bitmap, x: i32, y: i32, opts: FontRenderOpts); } pub trait Font { - type CharacterType: Character; + type CharacterType: Character; - fn character(&self, ch: char) -> &Self::CharacterType; - fn space_width(&self) -> u8; - fn line_height(&self) -> u8; - fn measure(&self, text: &str, opts: FontRenderOpts) -> (u32, u32); + fn character(&self, ch: char) -> &Self::CharacterType; + fn space_width(&self) -> u8; + fn line_height(&self) -> u8; + fn measure(&self, text: &str, opts: FontRenderOpts) -> (u32, u32); } #[derive(Debug, Clone, Eq, PartialEq)] pub struct BitmaskCharacter { - bytes: [u8; CHAR_HEIGHT], - bounds: Rect, + bytes: [u8; CHAR_HEIGHT], + bounds: Rect, } impl Character for BitmaskCharacter { - #[inline] - fn bounds(&self) -> &Rect { - &self.bounds - } + #[inline] + fn bounds(&self) -> &Rect { + &self.bounds + } - fn draw(&self, dest: &mut Bitmap, x: i32, y: i32, opts: FontRenderOpts) { - // out of bounds check - if ((x + self.bounds.width as i32) < dest.clip_region().x) - || ((y + self.bounds.height as i32) < dest.clip_region().y) - || (x >= dest.clip_region().right()) - || (y >= dest.clip_region().bottom()) - { - return; - } + fn draw(&self, dest: &mut Bitmap, x: i32, y: i32, opts: FontRenderOpts) { + // out of bounds check + if ((x + self.bounds.width as i32) < dest.clip_region().x) + || ((y + self.bounds.height as i32) < dest.clip_region().y) + || (x >= dest.clip_region().right()) + || (y >= dest.clip_region().bottom()) + { + return; + } - let color = match opts { - FontRenderOpts::Color(color) => color, - _ => 0, - }; + let color = match opts { + FontRenderOpts::Color(color) => color, + _ => 0, + }; - // TODO: i'm sure this can be optimized, lol - for char_y in 0..self.bounds.height as usize { - let mut bit_mask = 0x80; - for char_x in 0..self.bounds.width as usize { - if self.bytes[char_y] & bit_mask > 0 { - dest.set_pixel(x + char_x as i32, y + char_y as i32, color); - } - bit_mask >>= 1; - } - } - } + // TODO: i'm sure this can be optimized, lol + for char_y in 0..self.bounds.height as usize { + let mut bit_mask = 0x80; + for char_x in 0..self.bounds.width as usize { + if self.bytes[char_y] & bit_mask > 0 { + dest.set_pixel(x + char_x as i32, y + char_y as i32, color); + } + bit_mask >>= 1; + } + } + } } #[derive(Clone, Eq, PartialEq)] pub struct BitmaskFont { - characters: Box<[BitmaskCharacter]>, - line_height: u8, - space_width: u8, + characters: Box<[BitmaskCharacter]>, + line_height: u8, + space_width: u8, } impl std::fmt::Debug for BitmaskFont { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("BitmaskFont") - .field("line_height", &self.line_height) - .field("space_width", &self.space_width) - .field("characters.len()", &self.characters.len()) - .finish() - } + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("BitmaskFont") + .field("line_height", &self.line_height) + .field("space_width", &self.space_width) + .field("characters.len()", &self.characters.len()) + .finish() + } } impl BitmaskFont { - pub fn new_vga_font() -> Result { - BitmaskFont::load_from_bytes(&mut Cursor::new(VGA_FONT_BYTES)) - } + pub fn new_vga_font() -> Result { + BitmaskFont::load_from_bytes(&mut Cursor::new(VGA_FONT_BYTES)) + } - pub fn load_from_file(path: &Path) -> Result { - let f = File::open(path)?; - let mut reader = BufReader::new(f); + pub fn load_from_file(path: &Path) -> Result { + let f = File::open(path)?; + let mut reader = BufReader::new(f); - BitmaskFont::load_from_bytes(&mut reader) - } + BitmaskFont::load_from_bytes(&mut reader) + } - pub fn load_from_bytes(reader: &mut T) -> Result { - let mut characters: Vec = Vec::with_capacity(NUM_CHARS); + pub fn load_from_bytes(reader: &mut T) -> Result { + let mut characters: Vec = Vec::with_capacity(NUM_CHARS); - // read character bitmap data - for _ in 0..NUM_CHARS { - let mut buffer = [0u8; CHAR_HEIGHT]; - reader.read_exact(&mut buffer)?; - let character = BitmaskCharacter { - bytes: buffer, - // bounds are filled in below. ugh. - bounds: Rect { - x: 0, - y: 0, - width: 0, - height: 0, - }, - }; - characters.push(character); - } + // read character bitmap data + for _ in 0..NUM_CHARS { + let mut buffer = [0u8; CHAR_HEIGHT]; + reader.read_exact(&mut buffer)?; + let character = BitmaskCharacter { + bytes: buffer, + // bounds are filled in below. ugh. + bounds: Rect { + x: 0, + y: 0, + width: 0, + height: 0, + }, + }; + characters.push(character); + } - // read character widths (used for rendering) - for i in 0..NUM_CHARS { - characters[i].bounds.width = reader.read_u8()? as u32; - } + // read character widths (used for rendering) + for i in 0..NUM_CHARS { + characters[i].bounds.width = reader.read_u8()? as u32; + } - // read global font height (used for rendering) - let line_height = reader.read_u8()?; - for i in 0..NUM_CHARS { - characters[i].bounds.height = line_height as u32; - } + // read global font height (used for rendering) + let line_height = reader.read_u8()?; + for i in 0..NUM_CHARS { + characters[i].bounds.height = line_height as u32; + } - let space_width = characters[' ' as usize].bounds.width as u8; + let space_width = characters[' ' as usize].bounds.width as u8; - Ok(BitmaskFont { - characters: characters.into_boxed_slice(), - line_height, - space_width, - }) - } + Ok(BitmaskFont { + characters: characters.into_boxed_slice(), + line_height, + space_width, + }) + } - pub fn to_file(&self, path: &Path) -> Result<(), FontError> { - let f = File::create(path)?; - let mut writer = BufWriter::new(f); - self.to_bytes(&mut writer) - } + pub fn to_file(&self, path: &Path) -> Result<(), FontError> { + let f = File::create(path)?; + let mut writer = BufWriter::new(f); + self.to_bytes(&mut writer) + } - pub fn to_bytes(&self, writer: &mut T) -> Result<(), FontError> { - // write character bitmap data - for i in 0..NUM_CHARS { - writer.write_all(&self.characters[i].bytes)?; - } + pub fn to_bytes(&self, writer: &mut T) -> Result<(), FontError> { + // write character bitmap data + for i in 0..NUM_CHARS { + writer.write_all(&self.characters[i].bytes)?; + } - // write character widths - for i in 0..NUM_CHARS { - writer.write_u8(self.characters[i].bounds.width as u8)?; - } + // write character widths + for i in 0..NUM_CHARS { + writer.write_u8(self.characters[i].bounds.width as u8)?; + } - // write global font height - writer.write_u8(self.line_height)?; + // write global font height + writer.write_u8(self.line_height)?; - Ok(()) - } + Ok(()) + } } impl Font for BitmaskFont { - type CharacterType = BitmaskCharacter; + type CharacterType = BitmaskCharacter; - #[inline] - fn character(&self, ch: char) -> &Self::CharacterType { - &self.characters[ch as usize] - } + #[inline] + fn character(&self, ch: char) -> &Self::CharacterType { + &self.characters[ch as usize] + } - #[inline] - fn space_width(&self) -> u8 { - self.space_width - } + #[inline] + fn space_width(&self) -> u8 { + self.space_width + } - #[inline] - fn line_height(&self) -> u8 { - self.line_height - } + #[inline] + fn line_height(&self) -> u8 { + self.line_height + } - fn measure(&self, text: &str, _opts: FontRenderOpts) -> (u32, u32) { - if text.is_empty() { - return (0, 0); - } - let mut height = 0; - let mut width = 0; - let mut x = 0; - // trimming whitespace off the end because it won't be rendered (since it's whitespace) - // and thus, won't contribute to visible rendered output (what we're measuring) - for ch in text.trim_end().chars() { - match ch { - '\n' => { - if x == 0 { - height += self.line_height as u32; - } - width = std::cmp::max(width, x); - x = 0; - }, - '\r' => (), - ch => { - if x == 0 { - height += self.line_height as u32; - } - x += self.character(ch).bounds().width; - } - } - } - width = std::cmp::max(width, x); - (width, height) - } + fn measure(&self, text: &str, _opts: FontRenderOpts) -> (u32, u32) { + if text.is_empty() { + return (0, 0); + } + let mut height = 0; + let mut width = 0; + let mut x = 0; + // trimming whitespace off the end because it won't be rendered (since it's whitespace) + // and thus, won't contribute to visible rendered output (what we're measuring) + for ch in text.trim_end().chars() { + match ch { + '\n' => { + if x == 0 { + height += self.line_height as u32; + } + width = std::cmp::max(width, x); + x = 0; + } + '\r' => (), + ch => { + if x == 0 { + height += self.line_height as u32; + } + x += self.character(ch).bounds().width; + } + } + } + width = std::cmp::max(width, x); + (width, height) + } } #[cfg(test)] pub mod tests { - use super::*; + use super::*; - #[test] - pub fn load_font() -> Result<(), FontError> { - let font = BitmaskFont::load_from_file(Path::new("./assets/vga.fnt"))?; - assert_eq!(256, font.characters.len()); - assert_eq!(CHAR_FIXED_WIDTH as u8, font.space_width); - for character in font.characters.iter() { - assert_eq!(CHAR_FIXED_WIDTH as u8, character.bounds.width as u8); - assert_eq!(CHAR_HEIGHT, character.bytes.len()); - } + #[test] + pub fn load_font() -> Result<(), FontError> { + let font = BitmaskFont::load_from_file(Path::new("./assets/vga.fnt"))?; + assert_eq!(256, font.characters.len()); + assert_eq!(CHAR_FIXED_WIDTH as u8, font.space_width); + for character in font.characters.iter() { + assert_eq!(CHAR_FIXED_WIDTH as u8, character.bounds.width as u8); + assert_eq!(CHAR_HEIGHT, character.bytes.len()); + } - Ok(()) - } + Ok(()) + } - #[test] - pub fn measure_text() -> Result<(), FontError> { - { - let font = BitmaskFont::load_from_file(Path::new("./assets/vga.fnt"))?; + #[test] + pub fn measure_text() -> Result<(), FontError> { + { + let font = BitmaskFont::load_from_file(Path::new("./assets/vga.fnt"))?; - assert_eq!((40, 8), font.measure("Hello", FontRenderOpts::None)); - assert_eq!((40, 16), font.measure("Hello\nthere", FontRenderOpts::None)); - assert_eq!((88, 24), font.measure("longer line\nshort\nthe end", FontRenderOpts::None)); - assert_eq!((0, 0), font.measure("", FontRenderOpts::None)); - assert_eq!((0, 0), font.measure(" ", FontRenderOpts::None)); - assert_eq!((40, 16), font.measure("\nhello", FontRenderOpts::None)); - assert_eq!((0, 0), font.measure("\n", FontRenderOpts::None)); - assert_eq!((40, 8), font.measure("hello\n", FontRenderOpts::None)); - assert_eq!((40, 24), font.measure("hello\n\nthere", FontRenderOpts::None)); - } + assert_eq!((40, 8), font.measure("Hello", FontRenderOpts::None)); + assert_eq!((40, 16), font.measure("Hello\nthere", FontRenderOpts::None)); + assert_eq!((88, 24), font.measure("longer line\nshort\nthe end", FontRenderOpts::None)); + assert_eq!((0, 0), font.measure("", FontRenderOpts::None)); + assert_eq!((0, 0), font.measure(" ", FontRenderOpts::None)); + assert_eq!((40, 16), font.measure("\nhello", FontRenderOpts::None)); + assert_eq!((0, 0), font.measure("\n", FontRenderOpts::None)); + assert_eq!((40, 8), font.measure("hello\n", FontRenderOpts::None)); + assert_eq!((40, 24), font.measure("hello\n\nthere", FontRenderOpts::None)); + } - { - let font = BitmaskFont::load_from_file(Path::new("./test-assets/small.fnt"))?; + { + let font = BitmaskFont::load_from_file(Path::new("./test-assets/small.fnt"))?; - assert_eq!((22, 7), font.measure("Hello", FontRenderOpts::None)); - assert_eq!((24, 14), font.measure("Hello\nthere", FontRenderOpts::None)); - assert_eq!((50, 21), font.measure("longer line\nshort\nthe end", FontRenderOpts::None)); - assert_eq!((0, 0), font.measure("", FontRenderOpts::None)); - assert_eq!((0, 0), font.measure(" ", FontRenderOpts::None)); - assert_eq!((21, 14), font.measure("\nhello", FontRenderOpts::None)); - assert_eq!((0, 0), font.measure("\n", FontRenderOpts::None)); - assert_eq!((21, 7), font.measure("hello\n", FontRenderOpts::None)); - assert_eq!((24, 21), font.measure("hello\n\nthere", FontRenderOpts::None)); - } + assert_eq!((22, 7), font.measure("Hello", FontRenderOpts::None)); + assert_eq!((24, 14), font.measure("Hello\nthere", FontRenderOpts::None)); + assert_eq!((50, 21), font.measure("longer line\nshort\nthe end", FontRenderOpts::None)); + assert_eq!((0, 0), font.measure("", FontRenderOpts::None)); + assert_eq!((0, 0), font.measure(" ", FontRenderOpts::None)); + assert_eq!((21, 14), font.measure("\nhello", FontRenderOpts::None)); + assert_eq!((0, 0), font.measure("\n", FontRenderOpts::None)); + assert_eq!((21, 7), font.measure("hello\n", FontRenderOpts::None)); + assert_eq!((24, 21), font.measure("hello\n\nthere", FontRenderOpts::None)); + } - Ok(()) - } + Ok(()) + } } diff --git a/libretrogd/src/graphics/palette.rs b/libretrogd/src/graphics/palette.rs index 6ba57a1..d20198b 100644 --- a/libretrogd/src/graphics/palette.rs +++ b/libretrogd/src/graphics/palette.rs @@ -7,13 +7,14 @@ use std::path::Path; use byteorder::{ReadBytesExt, WriteBytesExt}; use thiserror::Error; -use crate::NUM_COLORS; use crate::graphics::*; +use crate::NUM_COLORS; use crate::utils::abs_diff; // silly "hack" (???) which allows us to alias the generic constraint `RangeBounds + Iterator` to `ColorRange` -pub trait ColorRange: RangeBounds + Iterator {} -impl ColorRange for T where T: RangeBounds + Iterator {} +pub trait ColorRange: RangeBounds + Iterator {} + +impl ColorRange for T where T: RangeBounds + Iterator {} pub static VGA_PALETTE_BYTES: &[u8] = include_bytes!("../../assets/vga.pal"); @@ -30,7 +31,7 @@ pub static VGA_PALETTE_BYTES: &[u8] = include_bytes!("../../assets/vga.pal"); /// returns: the u32 packed color #[inline] pub fn to_argb32(a: u8, r: u8, g: u8, b: u8) -> u32 { - (b as u32) + ((g as u32) << 8) + ((r as u32) << 16) + ((a as u32) << 24) + (b as u32) + ((g as u32) << 8) + ((r as u32) << 16) + ((a as u32) << 24) } /// Extracts the individual ARGB components out of a combined 32-bit color value which is in the @@ -43,11 +44,11 @@ pub fn to_argb32(a: u8, r: u8, g: u8, b: u8) -> u32 { /// returns: the individual ARGB color components (0-255 each) in order: alpha, red, green, blue #[inline] pub fn from_argb32(argb: u32) -> (u8, u8, u8, u8) { - let a = ((argb & 0xff000000) >> 24) as u8; - let r = ((argb & 0x00ff0000) >> 16) as u8; - let g = ((argb & 0x0000ff00) >> 8) as u8; - let b = (argb & 0x000000ff) as u8; - (a, r, g, b) + let a = ((argb & 0xff000000) >> 24) as u8; + let r = ((argb & 0x00ff0000) >> 16) as u8; + let g = ((argb & 0x0000ff00) >> 8) as u8; + let b = (argb & 0x000000ff) as u8; + (a, r, g, b) } /// Converts a set of individual RGB components to a combined 32-bit color value, packed into @@ -62,7 +63,7 @@ pub fn from_argb32(argb: u32) -> (u8, u8, u8, u8) { /// returns: the u32 packed color #[inline] pub fn to_rgb32(r: u8, g: u8, b: u8) -> u32 { - to_argb32(255, r, g, b) + to_argb32(255, r, g, b) } /// Extracts the individual RGB components out of a combined 32-bit color value which is in the @@ -75,11 +76,11 @@ pub fn to_rgb32(r: u8, g: u8, b: u8) -> u32 { /// returns: the individual ARGB color components (0-255 each) in order: red, green, blue #[inline] pub fn from_rgb32(rgb: u32) -> (u8, u8, u8) { - // ignore alpha component at 0xff000000 ... - let r = ((rgb & 0x00ff0000) >> 16) as u8; - let g = ((rgb & 0x0000ff00) >> 8) as u8; - let b = (rgb & 0x000000ff) as u8; - (r, g, b) + // ignore alpha component at 0xff000000 ... + let r = ((rgb & 0x00ff0000) >> 16) as u8; + let g = ((rgb & 0x0000ff00) >> 8) as u8; + let b = (rgb & 0x000000ff) as u8; + (r, g, b) } /// Linearly interpolates between two 32-bit packed colors in the format 0xAARRGGBB. @@ -91,14 +92,14 @@ pub fn from_rgb32(rgb: u32) -> (u8, u8, u8) { /// * `t`: the amount to interpolate between the two values, specified as a fraction. #[inline] pub fn lerp_argb32(a: u32, b: u32, t: f32) -> u32 { - let (a1, r1, g1, b1) = from_argb32(a); - let (a2, r2, g2, b2) = from_argb32(b); - to_argb32( - ((a1 as f32) + ((a2 as f32) - (a1 as f32)) * t) as u8, - ((r1 as f32) + ((r2 as f32) - (r1 as f32)) * t) as u8, - ((g1 as f32) + ((g2 as f32) - (g1 as f32)) * t) as u8, - ((b1 as f32) + ((b2 as f32) - (b1 as f32)) * t) as u8, - ) + let (a1, r1, g1, b1) = from_argb32(a); + let (a2, r2, g2, b2) = from_argb32(b); + to_argb32( + ((a1 as f32) + ((a2 as f32) - (a1 as f32)) * t) as u8, + ((r1 as f32) + ((r2 as f32) - (r1 as f32)) * t) as u8, + ((g1 as f32) + ((g2 as f32) - (g1 as f32)) * t) as u8, + ((b1 as f32) + ((b2 as f32) - (b1 as f32)) * t) as u8, + ) } /// Linearly interpolates between two 32-bit packed colors in the format 0xAARRGGBB. Ignores the @@ -111,13 +112,13 @@ pub fn lerp_argb32(a: u32, b: u32, t: f32) -> u32 { /// * `t`: the amount to interpolate between the two values, specified as a fraction. #[inline] pub fn lerp_rgb32(a: u32, b: u32, t: f32) -> u32 { - let (r1, g1, b1) = from_rgb32(a); - let (r2, g2, b2) = from_rgb32(b); - to_rgb32( - ((r1 as f32) + ((r2 as f32) - (r1 as f32)) * t) as u8, - ((g1 as f32) + ((g2 as f32) - (g1 as f32)) * t) as u8, - ((b1 as f32) + ((b2 as f32) - (b1 as f32)) * t) as u8, - ) + let (r1, g1, b1) = from_rgb32(a); + let (r2, g2, b2) = from_rgb32(b); + to_rgb32( + ((r1 as f32) + ((r2 as f32) - (r1 as f32)) * t) as u8, + ((g1 as f32) + ((g2 as f32) - (g1 as f32)) * t) as u8, + ((b1 as f32) + ((b2 as f32) - (b1 as f32)) * t) as u8, + ) } const LUMINANCE_RED: f32 = 0.212655; @@ -125,634 +126,634 @@ const LUMINANCE_GREEN: f32 = 0.715158; const LUMINANCE_BLUE: f32 = 0.072187; fn srgb_to_linearized(color_channel: u8) -> f32 { - let intensity = color_channel as f32 / 255.0; - if intensity <= 0.04045 { - intensity / 12.92 - } else { - ((intensity + 0.055) / (1.055)).powf(2.4) - } + let intensity = color_channel as f32 / 255.0; + if intensity <= 0.04045 { + intensity / 12.92 + } else { + ((intensity + 0.055) / (1.055)).powf(2.4) + } } /// Calculates the given sRGB color's luminance, returned as a value between 0.0 and 1.0. pub fn luminance(r: u8, g: u8, b: u8) -> f32 { - (LUMINANCE_RED * srgb_to_linearized(r)) + - (LUMINANCE_GREEN * srgb_to_linearized(g)) + - (LUMINANCE_BLUE * srgb_to_linearized(b)) + (LUMINANCE_RED * srgb_to_linearized(r)) + + (LUMINANCE_GREEN * srgb_to_linearized(g)) + + (LUMINANCE_BLUE * srgb_to_linearized(b)) } fn brightness(mut luminance: f32) -> f32 { - if luminance <= 0.0031308 { - luminance *= 12.92; - } else { - luminance = 1.055 * luminance.powf(1.0 / 2.4) - 0.055; - } - luminance + if luminance <= 0.0031308 { + luminance *= 12.92; + } else { + luminance = 1.055 * luminance.powf(1.0 / 2.4) - 0.055; + } + luminance } /// Calculates the approximate "brightness" / grey-scale value for the given sRGB color, returned /// as a value between 0 and 255. pub fn greyscale(r: u8, b: u8, g: u8) -> u8 { - (brightness(luminance(r, g, b)) * 255.0) as u8 + (brightness(luminance(r, g, b)) * 255.0) as u8 } // vga bios (0-63) format fn read_palette_6bit( - reader: &mut T, - num_colors: usize, + reader: &mut T, + num_colors: usize, ) -> Result<[u32; NUM_COLORS], PaletteError> { - if num_colors > NUM_COLORS { - return Err(PaletteError::OutOfRange(num_colors)) - } - let mut colors = [0u32; NUM_COLORS]; - for i in 0..num_colors { - let r = reader.read_u8()?; - let g = reader.read_u8()?; - let b = reader.read_u8()?; - let color = to_rgb32(r * 4, g * 4, b * 4); - colors[i as usize] = color; - } - Ok(colors) + if num_colors > NUM_COLORS { + return Err(PaletteError::OutOfRange(num_colors)); + } + let mut colors = [0u32; NUM_COLORS]; + for i in 0..num_colors { + let r = reader.read_u8()?; + let g = reader.read_u8()?; + let b = reader.read_u8()?; + let color = to_rgb32(r * 4, g * 4, b * 4); + colors[i as usize] = color; + } + Ok(colors) } fn write_palette_6bit( - writer: &mut T, - colors: &[u32; NUM_COLORS], - num_colors: usize, + writer: &mut T, + colors: &[u32; NUM_COLORS], + num_colors: usize, ) -> Result<(), PaletteError> { - if num_colors > NUM_COLORS { - return Err(PaletteError::OutOfRange(num_colors)) - } - for i in 0..num_colors { - let (r, g, b) = from_rgb32(colors[i as usize]); - writer.write_u8(r / 4)?; - writer.write_u8(g / 4)?; - writer.write_u8(b / 4)?; - } - Ok(()) + if num_colors > NUM_COLORS { + return Err(PaletteError::OutOfRange(num_colors)); + } + for i in 0..num_colors { + let (r, g, b) = from_rgb32(colors[i as usize]); + writer.write_u8(r / 4)?; + writer.write_u8(g / 4)?; + writer.write_u8(b / 4)?; + } + Ok(()) } // normal (0-255) format fn read_palette_8bit( - reader: &mut T, - num_colors: usize, + reader: &mut T, + num_colors: usize, ) -> Result<[u32; NUM_COLORS], PaletteError> { - if num_colors > NUM_COLORS { - return Err(PaletteError::OutOfRange(num_colors)) - } - let mut colors = [0u32; NUM_COLORS]; - for i in 0..num_colors { - let r = reader.read_u8()?; - let g = reader.read_u8()?; - let b = reader.read_u8()?; - let color = to_rgb32(r, g, b); - colors[i as usize] = color; - } - Ok(colors) + if num_colors > NUM_COLORS { + return Err(PaletteError::OutOfRange(num_colors)); + } + let mut colors = [0u32; NUM_COLORS]; + for i in 0..num_colors { + let r = reader.read_u8()?; + let g = reader.read_u8()?; + let b = reader.read_u8()?; + let color = to_rgb32(r, g, b); + colors[i as usize] = color; + } + Ok(colors) } fn write_palette_8bit( - writer: &mut T, - colors: &[u32; NUM_COLORS], - num_colors: usize, + writer: &mut T, + colors: &[u32; NUM_COLORS], + num_colors: usize, ) -> Result<(), PaletteError> { - if num_colors > NUM_COLORS { - return Err(PaletteError::OutOfRange(num_colors)) - } - for i in 0..num_colors { - let (r, g, b) = from_rgb32(colors[i as usize]); - writer.write_u8(r)?; - writer.write_u8(g)?; - writer.write_u8(b)?; - } - Ok(()) + if num_colors > NUM_COLORS { + return Err(PaletteError::OutOfRange(num_colors)); + } + for i in 0..num_colors { + let (r, g, b) = from_rgb32(colors[i as usize]); + writer.write_u8(r)?; + writer.write_u8(g)?; + writer.write_u8(b)?; + } + Ok(()) } #[derive(Error, Debug)] pub enum PaletteError { - #[error("Palette I/O error")] - IOError(#[from] std::io::Error), + #[error("Palette I/O error")] + IOError(#[from] std::io::Error), - #[error("Size or index is out of the supported range for palettes: {0}")] - OutOfRange(usize), + #[error("Size or index is out of the supported range for palettes: {0}")] + OutOfRange(usize), } pub enum PaletteFormat { - /// Individual RGB components in 6-bits (0-63) for VGA BIOS compatibility - Vga, - /// Individual RGB components in 8-bits (0-255) - Normal, + /// Individual RGB components in 6-bits (0-63) for VGA BIOS compatibility + Vga, + /// Individual RGB components in 8-bits (0-255) + Normal, } /// Contains a 256 color palette, and provides methods useful for working with palettes. The /// colors are all stored individually as 32-bit packed values in the format 0xAARRGGBB. #[derive(Debug, Clone, Eq, PartialEq)] pub struct Palette { - colors: [u32; NUM_COLORS], + colors: [u32; NUM_COLORS], } impl Palette { - /// Creates a new Palette with all black colors. - pub fn new() -> Palette { - Palette { - colors: [0; NUM_COLORS], - } - } + /// Creates a new Palette with all black colors. + pub fn new() -> Palette { + Palette { + colors: [0; NUM_COLORS], + } + } - /// Creates a new Palette with all initial colors having the RGB values specified. - pub fn new_with_default(r: u8, g: u8, b: u8) -> Palette { - let rgb = to_rgb32(r, g, b); - Palette { - colors: [rgb; NUM_COLORS], - } - } + /// Creates a new Palette with all initial colors having the RGB values specified. + pub fn new_with_default(r: u8, g: u8, b: u8) -> Palette { + let rgb = to_rgb32(r, g, b); + Palette { + colors: [rgb; NUM_COLORS], + } + } - /// Creates a new Palette, pre-loaded with the default VGA BIOS colors. - pub fn new_vga_palette() -> Result { - Palette::load_from_bytes(&mut Cursor::new(VGA_PALETTE_BYTES), PaletteFormat::Vga) - } + /// Creates a new Palette, pre-loaded with the default VGA BIOS colors. + pub fn new_vga_palette() -> Result { + Palette::load_from_bytes(&mut Cursor::new(VGA_PALETTE_BYTES), PaletteFormat::Vga) + } - /// Loads and returns a Palette from a palette file on disk. - /// - /// # Arguments - /// - /// * `path`: the path of the palette file to be loaded - /// * `format`: the format that the palette data is expected to be in - pub fn load_from_file(path: &Path, format: PaletteFormat) -> Result { - let f = File::open(path)?; - let mut reader = BufReader::new(f); - Self::load_from_bytes(&mut reader, format) - } + /// Loads and returns a Palette from a palette file on disk. + /// + /// # Arguments + /// + /// * `path`: the path of the palette file to be loaded + /// * `format`: the format that the palette data is expected to be in + pub fn load_from_file(path: &Path, format: PaletteFormat) -> Result { + let f = File::open(path)?; + let mut reader = BufReader::new(f); + Self::load_from_bytes(&mut reader, format) + } - /// Loads and returns a Palette from a reader. The data being loaded is expected to be the same - /// as if the palette was being loaded from a file on disk. - /// - /// # Arguments - /// - /// * `reader`: the reader to load the palette from - /// * `format`: the format that the palette data is expected to be in - pub fn load_from_bytes( - reader: &mut T, - format: PaletteFormat, - ) -> Result { - let colors = match format { - PaletteFormat::Vga => read_palette_6bit(reader, NUM_COLORS)?, - PaletteFormat::Normal => read_palette_8bit(reader, NUM_COLORS)?, - }; - Ok(Palette { colors }) - } + /// Loads and returns a Palette from a reader. The data being loaded is expected to be the same + /// as if the palette was being loaded from a file on disk. + /// + /// # Arguments + /// + /// * `reader`: the reader to load the palette from + /// * `format`: the format that the palette data is expected to be in + pub fn load_from_bytes( + reader: &mut T, + format: PaletteFormat, + ) -> Result { + let colors = match format { + PaletteFormat::Vga => read_palette_6bit(reader, NUM_COLORS)?, + PaletteFormat::Normal => read_palette_8bit(reader, NUM_COLORS)?, + }; + Ok(Palette { colors }) + } - /// Loads and returns a Palette from a palette file on disk, where the palette only contains - /// the number of colors specified, less than or equal to 256 otherwise an error is returned. - /// The remaining color entries will all be 0,0,0 (black) in the returned palette. - /// - /// # Arguments - /// - /// * `path`: the path of the palette file to be loaded - /// * `format`: the format that the palette data is expected to be in - /// * `num_colors`: the expected number of colors in the palette to be loaded (<= 256) - pub fn load_num_colors_from_file( - path: &Path, - format: PaletteFormat, - num_colors: usize, - ) -> Result { - let f = File::open(path)?; - let mut reader = BufReader::new(f); - Self::load_num_colors_from_bytes(&mut reader, format, num_colors) - } + /// Loads and returns a Palette from a palette file on disk, where the palette only contains + /// the number of colors specified, less than or equal to 256 otherwise an error is returned. + /// The remaining color entries will all be 0,0,0 (black) in the returned palette. + /// + /// # Arguments + /// + /// * `path`: the path of the palette file to be loaded + /// * `format`: the format that the palette data is expected to be in + /// * `num_colors`: the expected number of colors in the palette to be loaded (<= 256) + pub fn load_num_colors_from_file( + path: &Path, + format: PaletteFormat, + num_colors: usize, + ) -> Result { + let f = File::open(path)?; + let mut reader = BufReader::new(f); + Self::load_num_colors_from_bytes(&mut reader, format, num_colors) + } - /// Loads and returns a Palette from a reader. The data being loaded is expected to be the same - /// as if the palette was being loaded from a file on disk. The palette being read should only - /// contain the number of colors specified, less than or equal to 256 otherwise an error is - /// returned. The remaining color entries will all be 0,0,0 (black) in the returned palette. - /// - /// # Arguments - /// - /// * `reader`: the reader to load the palette from - /// * `format`: the format that the palette data is expected to be in - /// * `num_colors`: the expected number of colors in the palette to be loaded (<= 256) - pub fn load_num_colors_from_bytes( - reader: &mut T, - format: PaletteFormat, - num_colors: usize, - ) -> Result { - if num_colors > NUM_COLORS { - return Err(PaletteError::OutOfRange(num_colors)) - } - let colors = match format { - PaletteFormat::Vga => read_palette_6bit(reader, num_colors)?, - PaletteFormat::Normal => read_palette_8bit(reader, num_colors)?, - }; - Ok(Palette { colors }) - } + /// Loads and returns a Palette from a reader. The data being loaded is expected to be the same + /// as if the palette was being loaded from a file on disk. The palette being read should only + /// contain the number of colors specified, less than or equal to 256 otherwise an error is + /// returned. The remaining color entries will all be 0,0,0 (black) in the returned palette. + /// + /// # Arguments + /// + /// * `reader`: the reader to load the palette from + /// * `format`: the format that the palette data is expected to be in + /// * `num_colors`: the expected number of colors in the palette to be loaded (<= 256) + pub fn load_num_colors_from_bytes( + reader: &mut T, + format: PaletteFormat, + num_colors: usize, + ) -> Result { + if num_colors > NUM_COLORS { + return Err(PaletteError::OutOfRange(num_colors)); + } + let colors = match format { + PaletteFormat::Vga => read_palette_6bit(reader, num_colors)?, + PaletteFormat::Normal => read_palette_8bit(reader, num_colors)?, + }; + Ok(Palette { colors }) + } - /// Writes the palette to a file on disk. If the file already exists, it will be overwritten. - /// - /// # Arguments - /// - /// * `path`: the path of the file to save the palette to - /// * `format`: the format to write the palette data in - pub fn to_file(&self, path: &Path, format: PaletteFormat) -> Result<(), PaletteError> { - let f = File::create(path)?; - let mut writer = BufWriter::new(f); - self.to_bytes(&mut writer, format) - } + /// Writes the palette to a file on disk. If the file already exists, it will be overwritten. + /// + /// # Arguments + /// + /// * `path`: the path of the file to save the palette to + /// * `format`: the format to write the palette data in + pub fn to_file(&self, path: &Path, format: PaletteFormat) -> Result<(), PaletteError> { + let f = File::create(path)?; + let mut writer = BufWriter::new(f); + self.to_bytes(&mut writer, format) + } - /// Writes the palette to a writer, in the same format as if it was writing to a file on disk. - /// - /// # Arguments - /// - /// * `writer`: the writer to write palette data to - /// * `format`: the format to write the palette data in - pub fn to_bytes( - &self, - writer: &mut T, - format: PaletteFormat, - ) -> Result<(), PaletteError> { - match format { - PaletteFormat::Vga => write_palette_6bit(writer, &self.colors, NUM_COLORS), - PaletteFormat::Normal => write_palette_8bit(writer, &self.colors, NUM_COLORS), - } - } + /// Writes the palette to a writer, in the same format as if it was writing to a file on disk. + /// + /// # Arguments + /// + /// * `writer`: the writer to write palette data to + /// * `format`: the format to write the palette data in + pub fn to_bytes( + &self, + writer: &mut T, + format: PaletteFormat, + ) -> Result<(), PaletteError> { + match format { + PaletteFormat::Vga => write_palette_6bit(writer, &self.colors, NUM_COLORS), + PaletteFormat::Normal => write_palette_8bit(writer, &self.colors, NUM_COLORS), + } + } - /// Writes the palette to a file on disk. If the file already exists, it will be overwritten. - /// Will only write out the specified number of colors to the palette file being written, - /// starting from the first color in the palette always. If the color count specified is - /// greater than 256 an error is returned. - /// - /// # Arguments - /// - /// * `path`: the path of the file to save the palette to - /// * `format`: the format to write the palette data in - /// * `num_colors`: the number of colors from this palette to write out to the file (<= 256) - pub fn num_colors_to_file( - &self, - path: &Path, - format: PaletteFormat, - num_colors: usize, - ) -> Result<(), PaletteError> { - if num_colors > NUM_COLORS { - return Err(PaletteError::OutOfRange(num_colors)) - } - let f = File::create(path)?; - let mut writer = BufWriter::new(f); - self.num_colors_to_bytes(&mut writer, format, num_colors) - } + /// Writes the palette to a file on disk. If the file already exists, it will be overwritten. + /// Will only write out the specified number of colors to the palette file being written, + /// starting from the first color in the palette always. If the color count specified is + /// greater than 256 an error is returned. + /// + /// # Arguments + /// + /// * `path`: the path of the file to save the palette to + /// * `format`: the format to write the palette data in + /// * `num_colors`: the number of colors from this palette to write out to the file (<= 256) + pub fn num_colors_to_file( + &self, + path: &Path, + format: PaletteFormat, + num_colors: usize, + ) -> Result<(), PaletteError> { + if num_colors > NUM_COLORS { + return Err(PaletteError::OutOfRange(num_colors)); + } + let f = File::create(path)?; + let mut writer = BufWriter::new(f); + self.num_colors_to_bytes(&mut writer, format, num_colors) + } - /// Writes the palette to a writer, in the same format as if it was writing to a file on disk. - /// Will only write out the specified number of colors to the writer, starting from the first - /// color in the palette always. If the color count specified is greater than 256 an error is - /// returned. - /// - /// # Arguments - /// - /// * `writer`: the writer to write palette data to - /// * `format`: the format to write the palette data in - /// * `num_colors`: the number of colors from this palette to write out (<= 256) - pub fn num_colors_to_bytes( - &self, - writer: &mut T, - format: PaletteFormat, - num_colors: usize, - ) -> Result<(), PaletteError> { - if num_colors > NUM_COLORS { - return Err(PaletteError::OutOfRange(num_colors)) - } - match format { - PaletteFormat::Vga => write_palette_6bit(writer, &self.colors, num_colors), - PaletteFormat::Normal => write_palette_8bit(writer, &self.colors, num_colors), - } - } + /// Writes the palette to a writer, in the same format as if it was writing to a file on disk. + /// Will only write out the specified number of colors to the writer, starting from the first + /// color in the palette always. If the color count specified is greater than 256 an error is + /// returned. + /// + /// # Arguments + /// + /// * `writer`: the writer to write palette data to + /// * `format`: the format to write the palette data in + /// * `num_colors`: the number of colors from this palette to write out (<= 256) + pub fn num_colors_to_bytes( + &self, + writer: &mut T, + format: PaletteFormat, + num_colors: usize, + ) -> Result<(), PaletteError> { + if num_colors > NUM_COLORS { + return Err(PaletteError::OutOfRange(num_colors)); + } + match format { + PaletteFormat::Vga => write_palette_6bit(writer, &self.colors, num_colors), + PaletteFormat::Normal => write_palette_8bit(writer, &self.colors, num_colors), + } + } - /// Fades a single color in the palette from its current RGB values towards the given RGB - /// values by up to the step amount given. This function is intended to be run many times - /// over a number of frames where each run completes a small step towards the complete fade. - /// - /// # Arguments - /// - /// * `color`: the color index to fade - /// * `target_r`: the target red component (0-255) to fade towards - /// * `target_g`: the target green component (0-255) to fade towards - /// * `target_b`: the target blue component (0-255) to fade towards - /// * `step`: the amount to "step" by towards the target RGB values - /// - /// returns: true if the color has reached the target RGB values, false otherwise - pub fn fade_color_toward_rgb( - &mut self, - color: u8, - target_r: u8, - target_g: u8, - target_b: u8, - step: u8, - ) -> bool { - let mut modified = false; + /// Fades a single color in the palette from its current RGB values towards the given RGB + /// values by up to the step amount given. This function is intended to be run many times + /// over a number of frames where each run completes a small step towards the complete fade. + /// + /// # Arguments + /// + /// * `color`: the color index to fade + /// * `target_r`: the target red component (0-255) to fade towards + /// * `target_g`: the target green component (0-255) to fade towards + /// * `target_b`: the target blue component (0-255) to fade towards + /// * `step`: the amount to "step" by towards the target RGB values + /// + /// returns: true if the color has reached the target RGB values, false otherwise + pub fn fade_color_toward_rgb( + &mut self, + color: u8, + target_r: u8, + target_g: u8, + target_b: u8, + step: u8, + ) -> bool { + let mut modified = false; - let (mut r, mut g, mut b) = from_rgb32(self.colors[color as usize]); + let (mut r, mut g, mut b) = from_rgb32(self.colors[color as usize]); - if r != target_r { - modified = true; - let diff_r = r.overflowing_sub(target_r).0; - if r > target_r { - r -= min(step, diff_r); - } else { - r += min(step, diff_r); - } - } + if r != target_r { + modified = true; + let diff_r = r.overflowing_sub(target_r).0; + if r > target_r { + r -= min(step, diff_r); + } else { + r += min(step, diff_r); + } + } - if g != target_g { - modified = true; - let diff_g = g.overflowing_sub(target_g).0; - if g > target_g { - g -= min(step, diff_g); - } else { - g += min(step, diff_g); - } - } + if g != target_g { + modified = true; + let diff_g = g.overflowing_sub(target_g).0; + if g > target_g { + g -= min(step, diff_g); + } else { + g += min(step, diff_g); + } + } - if b != target_b { - modified = true; - let diff_b = b.overflowing_sub(target_b).0; - if b > target_b { - b -= min(step, diff_b); - } else { - b += min(step, diff_b); - } - } + if b != target_b { + modified = true; + let diff_b = b.overflowing_sub(target_b).0; + if b > target_b { + b -= min(step, diff_b); + } else { + b += min(step, diff_b); + } + } - if modified { - self.colors[color as usize] = to_rgb32(r, g, b); - } + if modified { + self.colors[color as usize] = to_rgb32(r, g, b); + } - (target_r == r) && (target_g == g) && (target_b == b) - } + (target_r == r) && (target_g == g) && (target_b == b) + } - /// Fades a range of colors in the palette from their current RGB values all towards the given - /// RGB values by up to the step amount given. This function is intended to be run many times - /// over a number of frames where each run completes a small step towards the complete fade. - /// - /// # Arguments - /// - /// * `colors`: the range of colors to be faded - /// * `target_r`: the target red component (0-255) to fade towards - /// * `target_g`: the target green component (0-255) to fade towards - /// * `target_b`: the target blue component (0-255) to fade towards - /// * `step`: the amount to "step" by towards the target RGB values - /// - /// returns: true if all of the colors in the range have reached the target RGB values, false - /// otherwise - pub fn fade_colors_toward_rgb( - &mut self, - colors: T, - target_r: u8, - target_g: u8, - target_b: u8, - step: u8, - ) -> bool { - let mut all_faded = true; - for color in colors { - if !self.fade_color_toward_rgb(color, target_r, target_g, target_b, step) { - all_faded = false; - } - } - all_faded - } + /// Fades a range of colors in the palette from their current RGB values all towards the given + /// RGB values by up to the step amount given. This function is intended to be run many times + /// over a number of frames where each run completes a small step towards the complete fade. + /// + /// # Arguments + /// + /// * `colors`: the range of colors to be faded + /// * `target_r`: the target red component (0-255) to fade towards + /// * `target_g`: the target green component (0-255) to fade towards + /// * `target_b`: the target blue component (0-255) to fade towards + /// * `step`: the amount to "step" by towards the target RGB values + /// + /// returns: true if all of the colors in the range have reached the target RGB values, false + /// otherwise + pub fn fade_colors_toward_rgb( + &mut self, + colors: T, + target_r: u8, + target_g: u8, + target_b: u8, + step: u8, + ) -> bool { + let mut all_faded = true; + for color in colors { + if !self.fade_color_toward_rgb(color, target_r, target_g, target_b, step) { + all_faded = false; + } + } + all_faded + } - /// Fades a range of colors in the palette from their current RGB values all towards the RGB - /// values in the other palette specified, by up to the step amount given. This function is - /// intended to be run many times over a number of frames where each run completes a small step - /// towards the complete fade. - /// - /// # Arguments - /// - /// * `colors`: the range of colors to be faded - /// * `palette`: the other palette to use as the target to fade towards - /// * `step`: the amount to "step" by towards the target RGB values - /// - /// returns: true if all of the colors in the range have reached the RGB values from the other - /// target palette, false otherwise - pub fn fade_colors_toward_palette( - &mut self, - colors: T, - palette: &Palette, - step: u8, - ) -> bool { - let mut all_faded = true; - for color in colors { - let (r, g, b) = from_rgb32(palette[color]); - if !self.fade_color_toward_rgb(color, r, g, b, step) { - all_faded = false; - } - } - all_faded - } + /// Fades a range of colors in the palette from their current RGB values all towards the RGB + /// values in the other palette specified, by up to the step amount given. This function is + /// intended to be run many times over a number of frames where each run completes a small step + /// towards the complete fade. + /// + /// # Arguments + /// + /// * `colors`: the range of colors to be faded + /// * `palette`: the other palette to use as the target to fade towards + /// * `step`: the amount to "step" by towards the target RGB values + /// + /// returns: true if all of the colors in the range have reached the RGB values from the other + /// target palette, false otherwise + pub fn fade_colors_toward_palette( + &mut self, + colors: T, + palette: &Palette, + step: u8, + ) -> bool { + let mut all_faded = true; + for color in colors { + let (r, g, b) = from_rgb32(palette[color]); + if !self.fade_color_toward_rgb(color, r, g, b, step) { + all_faded = false; + } + } + all_faded + } - /// Linearly interpolates between the specified colors in two palettes, storing the - /// interpolation results in this palette. - /// - /// # Arguments - /// - /// * `colors`: the range of colors to be interpolated - /// * `a`: the first palette - /// * `b`: the second palette - /// * `t`: the amount to interpolate between the two palettes, specified as a fraction - pub fn lerp(&mut self, colors: T, a: &Palette, b: &Palette, t: f32) { - for color in colors { - self[color] = lerp_rgb32(a[color], b[color], t); - } - } + /// Linearly interpolates between the specified colors in two palettes, storing the + /// interpolation results in this palette. + /// + /// # Arguments + /// + /// * `colors`: the range of colors to be interpolated + /// * `a`: the first palette + /// * `b`: the second palette + /// * `t`: the amount to interpolate between the two palettes, specified as a fraction + pub fn lerp(&mut self, colors: T, a: &Palette, b: &Palette, t: f32) { + for color in colors { + self[color] = lerp_rgb32(a[color], b[color], t); + } + } - /// Rotates a range of colors in the palette by a given amount. - /// - /// # Arguments - /// - /// * `colors`: the range of colors to be rotated - /// * `step`: the number of positions (and direction) to rotate all colors by - pub fn rotate_colors(&mut self, colors: T, step: i8) { - use Bound::*; - let start = match colors.start_bound() { - Excluded(&start) => start + 1, - Included(&start) => start, - Unbounded => 0, - } as usize; - let end = match colors.end_bound() { - Excluded(&end) => end - 1, - Included(&end) => end, - Unbounded => 255, - } as usize; - let subset = &mut self.colors[start..=end]; - match step.signum() { - -1 => subset.rotate_left(step.abs() as usize), - 1 => subset.rotate_right(step.abs() as usize), - _ => {} - } - } + /// Rotates a range of colors in the palette by a given amount. + /// + /// # Arguments + /// + /// * `colors`: the range of colors to be rotated + /// * `step`: the number of positions (and direction) to rotate all colors by + pub fn rotate_colors(&mut self, colors: T, step: i8) { + use Bound::*; + let start = match colors.start_bound() { + Excluded(&start) => start + 1, + Included(&start) => start, + Unbounded => 0, + } as usize; + let end = match colors.end_bound() { + Excluded(&end) => end - 1, + Included(&end) => end, + Unbounded => 255, + } as usize; + let subset = &mut self.colors[start..=end]; + match step.signum() { + -1 => subset.rotate_left(step.abs() as usize), + 1 => subset.rotate_right(step.abs() as usize), + _ => {} + } + } - /// Finds and returns the index of the closest color in this palette to the RGB values provided. - /// This will not always return great results. It depends largely on the palette and the RGB - /// values being searched (for example, searching for bright green 0,255,0 in a palette which - /// contains no green hues at all is not likely to return a useful result). - pub fn find_color(&self, r: u8, g: u8, b: u8) -> u8 { - let mut closest_distance = 255 * 3; - let mut closest = 0; + /// Finds and returns the index of the closest color in this palette to the RGB values provided. + /// This will not always return great results. It depends largely on the palette and the RGB + /// values being searched (for example, searching for bright green 0,255,0 in a palette which + /// contains no green hues at all is not likely to return a useful result). + pub fn find_color(&self, r: u8, g: u8, b: u8) -> u8 { + let mut closest_distance = 255 * 3; + let mut closest = 0; - for (index, color) in self.colors.iter().enumerate() { - let (this_r, this_g, this_b) = from_rgb32(*color); + for (index, color) in self.colors.iter().enumerate() { + let (this_r, this_g, this_b) = from_rgb32(*color); - if r == this_r && g == this_g && b == this_b { - return index as u8; - } else { - // this comparison method is using the sRGB Euclidean formula described here: - // https://en.wikipedia.org/wiki/Color_difference + if r == this_r && g == this_g && b == this_b { + return index as u8; + } else { + // this comparison method is using the sRGB Euclidean formula described here: + // https://en.wikipedia.org/wiki/Color_difference - let distance = abs_diff(this_r, r) as u32 - + abs_diff(this_g, g) as u32 - + abs_diff(this_b, b) as u32; + let distance = abs_diff(this_r, r) as u32 + + abs_diff(this_g, g) as u32 + + abs_diff(this_b, b) as u32; - if distance < closest_distance { - closest = index as u8; - closest_distance = distance; - } - } - } + if distance < closest_distance { + closest = index as u8; + closest_distance = distance; + } + } + } - closest - } + closest + } - /// Debug helper that draws this palette to the given bitmap as a 16x16 pixel grid, where each - /// pixel is one of the colors from this palette, in ascending order, left-to-right, - /// top-to-bottom. The coordinates given specify the top-left coordinate on the destination - /// bitmap to begin drawing the palette at. - pub fn draw(&self, dest: &mut Bitmap, x: i32, y: i32) { - let mut color = 0; - for yd in 0..16 { - for xd in 0..16 { - dest.set_pixel(x + xd, y + yd, color); - color = color.wrapping_add(1); - } - } - } + /// Debug helper that draws this palette to the given bitmap as a 16x16 pixel grid, where each + /// pixel is one of the colors from this palette, in ascending order, left-to-right, + /// top-to-bottom. The coordinates given specify the top-left coordinate on the destination + /// bitmap to begin drawing the palette at. + pub fn draw(&self, dest: &mut Bitmap, x: i32, y: i32) { + let mut color = 0; + for yd in 0..16 { + for xd in 0..16 { + dest.set_pixel(x + xd, y + yd, color); + color = color.wrapping_add(1); + } + } + } } impl Index for Palette { - type Output = u32; + type Output = u32; - #[inline] - fn index(&self, index: u8) -> &Self::Output { - &self.colors[index as usize] - } + #[inline] + fn index(&self, index: u8) -> &Self::Output { + &self.colors[index as usize] + } } impl IndexMut for Palette { - #[inline] - fn index_mut(&mut self, index: u8) -> &mut Self::Output { - &mut self.colors[index as usize] - } + #[inline] + fn index_mut(&mut self, index: u8) -> &mut Self::Output { + &mut self.colors[index as usize] + } } #[cfg(test)] mod tests { - use tempfile::TempDir; + use tempfile::TempDir; - use super::*; + use super::*; - #[test] - fn argb_conversions() { - let argb = to_argb32(0x11, 0x22, 0x33, 0x44); - assert_eq!(argb, 0x11223344); + #[test] + fn argb_conversions() { + let argb = to_argb32(0x11, 0x22, 0x33, 0x44); + assert_eq!(argb, 0x11223344); - let argb = to_rgb32(0x22, 0x33, 0x44); - assert_eq!(argb, 0xff223344); + let argb = to_rgb32(0x22, 0x33, 0x44); + assert_eq!(argb, 0xff223344); - let (a, r, g, b) = from_argb32(0x11223344); - assert_eq!(0x11, a); - assert_eq!(0x22, r); - assert_eq!(0x33, g); - assert_eq!(0x44, b); + let (a, r, g, b) = from_argb32(0x11223344); + assert_eq!(0x11, a); + assert_eq!(0x22, r); + assert_eq!(0x33, g); + assert_eq!(0x44, b); - let (r, g, b) = from_rgb32(0x11223344); - assert_eq!(0x22, r); - assert_eq!(0x33, g); - assert_eq!(0x44, b); - } + let (r, g, b) = from_rgb32(0x11223344); + assert_eq!(0x22, r); + assert_eq!(0x33, g); + assert_eq!(0x44, b); + } - #[test] - fn get_and_set_colors() { - let mut palette = Palette::new(); - assert_eq!(0, palette[0]); - assert_eq!(0, palette[1]); - palette[0] = 0x11223344; - assert_eq!(0x11223344, palette[0]); - assert_eq!(0, palette[1]); - } + #[test] + fn get_and_set_colors() { + let mut palette = Palette::new(); + assert_eq!(0, palette[0]); + assert_eq!(0, palette[1]); + palette[0] = 0x11223344; + assert_eq!(0x11223344, palette[0]); + assert_eq!(0, palette[1]); + } - fn assert_ega_colors(palette: &Palette) { - assert_eq!(0xff000000, palette[0]); - assert_eq!(0xff0000a8, palette[1]); - assert_eq!(0xff00a800, palette[2]); - assert_eq!(0xff00a8a8, palette[3]); - assert_eq!(0xffa80000, palette[4]); - assert_eq!(0xffa800a8, palette[5]); - assert_eq!(0xffa85400, palette[6]); - assert_eq!(0xffa8a8a8, palette[7]); - assert_eq!(0xff545454, palette[8]); - assert_eq!(0xff5454fc, palette[9]); - assert_eq!(0xff54fc54, palette[10]); - assert_eq!(0xff54fcfc, palette[11]); - assert_eq!(0xfffc5454, palette[12]); - assert_eq!(0xfffc54fc, palette[13]); - assert_eq!(0xfffcfc54, palette[14]); - assert_eq!(0xfffcfcfc, palette[15]); - } + fn assert_ega_colors(palette: &Palette) { + assert_eq!(0xff000000, palette[0]); + assert_eq!(0xff0000a8, palette[1]); + assert_eq!(0xff00a800, palette[2]); + assert_eq!(0xff00a8a8, palette[3]); + assert_eq!(0xffa80000, palette[4]); + assert_eq!(0xffa800a8, palette[5]); + assert_eq!(0xffa85400, palette[6]); + assert_eq!(0xffa8a8a8, palette[7]); + assert_eq!(0xff545454, palette[8]); + assert_eq!(0xff5454fc, palette[9]); + assert_eq!(0xff54fc54, palette[10]); + assert_eq!(0xff54fcfc, palette[11]); + assert_eq!(0xfffc5454, palette[12]); + assert_eq!(0xfffc54fc, palette[13]); + assert_eq!(0xfffcfc54, palette[14]); + assert_eq!(0xfffcfcfc, palette[15]); + } - #[test] - fn load_and_save() -> Result<(), PaletteError> { - let tmp_dir = TempDir::new()?; + #[test] + fn load_and_save() -> Result<(), PaletteError> { + let tmp_dir = TempDir::new()?; - // vga rgb format (6-bit) + // vga rgb format (6-bit) - let palette = Palette::load_from_file(Path::new("./assets/vga.pal"), PaletteFormat::Vga)?; - assert_ega_colors(&palette); + let palette = Palette::load_from_file(Path::new("./assets/vga.pal"), PaletteFormat::Vga)?; + assert_ega_colors(&palette); - let save_path = tmp_dir.path().join("test_save_vga_format.pal"); - palette.to_file(&save_path, PaletteFormat::Vga)?; - let reloaded_palette = Palette::load_from_file(&save_path, PaletteFormat::Vga)?; - assert_eq!(palette, reloaded_palette); + let save_path = tmp_dir.path().join("test_save_vga_format.pal"); + palette.to_file(&save_path, PaletteFormat::Vga)?; + let reloaded_palette = Palette::load_from_file(&save_path, PaletteFormat::Vga)?; + assert_eq!(palette, reloaded_palette); - // normal rgb format (8-bit) + // normal rgb format (8-bit) - let palette = - Palette::load_from_file(Path::new("./test-assets/dp2.pal"), PaletteFormat::Normal)?; + let palette = + Palette::load_from_file(Path::new("./test-assets/dp2.pal"), PaletteFormat::Normal)?; - let save_path = tmp_dir.path().join("test_save_normal_format.pal"); - palette.to_file(&save_path, PaletteFormat::Normal)?; - let reloaded_palette = Palette::load_from_file(&save_path, PaletteFormat::Normal)?; - assert_eq!(palette, reloaded_palette); + let save_path = tmp_dir.path().join("test_save_normal_format.pal"); + palette.to_file(&save_path, PaletteFormat::Normal)?; + let reloaded_palette = Palette::load_from_file(&save_path, PaletteFormat::Normal)?; + assert_eq!(palette, reloaded_palette); - Ok(()) - } + Ok(()) + } - #[test] - fn load_and_save_arbitrary_color_count() -> Result<(), PaletteError> { - let tmp_dir = TempDir::new()?; + #[test] + fn load_and_save_arbitrary_color_count() -> Result<(), PaletteError> { + let tmp_dir = TempDir::new()?; - // vga rgb format (6-bit) + // vga rgb format (6-bit) - let palette = Palette::load_num_colors_from_file(Path::new("./test-assets/ega_6bit.pal"), PaletteFormat::Vga, 16)?; - assert_ega_colors(&palette); + let palette = Palette::load_num_colors_from_file(Path::new("./test-assets/ega_6bit.pal"), PaletteFormat::Vga, 16)?; + assert_ega_colors(&palette); - let save_path = tmp_dir.path().join("test_save_vga_format_16_colors.pal"); - palette.num_colors_to_file(&save_path, PaletteFormat::Vga, 16)?; - let reloaded_palette = Palette::load_num_colors_from_file(&save_path, PaletteFormat::Vga, 16)?; - assert_eq!(palette, reloaded_palette); + let save_path = tmp_dir.path().join("test_save_vga_format_16_colors.pal"); + palette.num_colors_to_file(&save_path, PaletteFormat::Vga, 16)?; + let reloaded_palette = Palette::load_num_colors_from_file(&save_path, PaletteFormat::Vga, 16)?; + assert_eq!(palette, reloaded_palette); - // normal rgb format (8-bit) + // normal rgb format (8-bit) - let palette = Palette::load_num_colors_from_file(Path::new("./test-assets/ega_8bit.pal"), PaletteFormat::Normal, 16)?; + let palette = Palette::load_num_colors_from_file(Path::new("./test-assets/ega_8bit.pal"), PaletteFormat::Normal, 16)?; - let save_path = tmp_dir.path().join("test_save_normal_format_16_colors.pal"); - palette.to_file(&save_path, PaletteFormat::Normal)?; - let reloaded_palette = Palette::load_num_colors_from_file(&save_path, PaletteFormat::Normal, 16)?; - assert_eq!(palette, reloaded_palette); + let save_path = tmp_dir.path().join("test_save_normal_format_16_colors.pal"); + palette.to_file(&save_path, PaletteFormat::Normal)?; + let reloaded_palette = Palette::load_num_colors_from_file(&save_path, PaletteFormat::Normal, 16)?; + assert_eq!(palette, reloaded_palette); - Ok(()) - } + Ok(()) + } } diff --git a/libretrogd/src/lib.rs b/libretrogd/src/lib.rs index 3f89495..e2bc449 100644 --- a/libretrogd/src/lib.rs +++ b/libretrogd/src/lib.rs @@ -12,33 +12,33 @@ pub mod system; pub mod utils; pub const LOW_RES: bool = if cfg!(feature = "low_res") { - true + true } else { - false + false }; pub const WIDE_SCREEN: bool = if cfg!(feature = "wide") { - true + true } else { - false + false }; pub const SCREEN_WIDTH: u32 = if cfg!(feature = "low_res") { - if cfg!(feature = "wide") { - 214 - } else { - 160 - } + if cfg!(feature = "wide") { + 214 + } else { + 160 + } } else { - if cfg!(feature = "wide") { - 428 - } else { - 320 - } + if cfg!(feature = "wide") { + 428 + } else { + 320 + } }; pub const SCREEN_HEIGHT: u32 = if cfg!(feature = "low_res") { - 120 + 120 } else { - 240 + 240 }; pub const SCREEN_TOP: u32 = 0; @@ -47,9 +47,9 @@ pub const SCREEN_RIGHT: u32 = SCREEN_WIDTH - 1; pub const SCREEN_BOTTOM: u32 = SCREEN_HEIGHT - 1; pub const DEFAULT_SCALE_FACTOR: u32 = if cfg!(feature = "low_res") { - 6 + 6 } else { - 3 + 3 }; pub const NUM_COLORS: usize = 256; // i mean ... the number of colors is really defined by the size of u8 ... diff --git a/libretrogd/src/math/circle.rs b/libretrogd/src/math/circle.rs index e3a3faf..caf2b2a 100644 --- a/libretrogd/src/math/circle.rs +++ b/libretrogd/src/math/circle.rs @@ -3,111 +3,111 @@ use crate::math::*; /// Represents a 2D circle, using integer center coordinates and radius. #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub struct Circle { - pub x: i32, - pub y: i32, - pub radius: u32, + pub x: i32, + pub y: i32, + pub radius: u32, } impl Circle { - #[inline] - pub fn new(x: i32, y: i32, radius: u32) -> Self { - Circle { x, y, radius } - } + #[inline] + pub fn new(x: i32, y: i32, radius: u32) -> Self { + Circle { x, y, radius } + } - pub fn new_encapsulating(points: &[Vector2]) -> Option { - if points.is_empty() { - return None; - } + pub fn new_encapsulating(points: &[Vector2]) -> Option { + if points.is_empty() { + return None; + } - let mut min_x = points[0].x; - let mut min_y = points[0].y; - let mut max_x = min_x; - let mut max_y = min_y; + let mut min_x = points[0].x; + let mut min_y = points[0].y; + let mut max_x = min_x; + let mut max_y = min_y; - for i in 0..points.len() { - let point = &points[i]; - min_x = point.x.min(min_x); - min_y = point.y.min(min_y); - max_x = point.x.max(max_x); - max_y = point.y.max(max_y); - } + for i in 0..points.len() { + let point = &points[i]; + min_x = point.x.min(min_x); + min_y = point.y.min(min_y); + max_x = point.x.max(max_x); + max_y = point.y.max(max_y); + } - let radius = distance_between(min_x, min_y, max_x, max_y) * 0.5; - let center_x = (max_x - min_x) / 2.0; - let center_y = (max_y - min_y) / 2.0; + let radius = distance_between(min_x, min_y, max_x, max_y) * 0.5; + let center_x = (max_x - min_x) / 2.0; + let center_y = (max_y - min_y) / 2.0; - Some(Circle { - x: center_x as i32, - y: center_y as i32, - radius: radius as u32, - }) - } + Some(Circle { + x: center_x as i32, + y: center_y as i32, + radius: radius as u32, + }) + } - /// Calculates the diameter of the circle. - #[inline] - pub fn diameter(&self) -> u32 { - self.radius * 2 - } + /// Calculates the diameter of the circle. + #[inline] + pub fn diameter(&self) -> u32 { + self.radius * 2 + } - /// Returns true if the given point is contained within the bounds of this circle. - pub fn contains_point(&self, x: i32, y: i32) -> bool { - let distance_squared = - distance_squared_between(self.x as f32, self.y as f32, x as f32, y as f32); - let radius_squared = (self.radius * self.radius) as f32; - distance_squared <= radius_squared - } + /// Returns true if the given point is contained within the bounds of this circle. + pub fn contains_point(&self, x: i32, y: i32) -> bool { + let distance_squared = + distance_squared_between(self.x as f32, self.y as f32, x as f32, y as f32); + let radius_squared = (self.radius * self.radius) as f32; + distance_squared <= radius_squared + } - /// Returns true if the given circle at least partially overlaps the bounds of this circle. - pub fn overlaps(&self, other: &Circle) -> bool { - let distance_squared = - distance_squared_between(self.x as f32, self.y as f32, other.x as f32, other.y as f32); - let minimum_distance_squared = - ((self.radius + other.radius) * (self.radius + other.radius)) as f32; - distance_squared <= minimum_distance_squared - } + /// Returns true if the given circle at least partially overlaps the bounds of this circle. + pub fn overlaps(&self, other: &Circle) -> bool { + let distance_squared = + distance_squared_between(self.x as f32, self.y as f32, other.x as f32, other.y as f32); + let minimum_distance_squared = + ((self.radius + other.radius) * (self.radius + other.radius)) as f32; + distance_squared <= minimum_distance_squared + } } #[cfg(test)] pub mod tests { - use super::*; + use super::*; - #[test] - pub fn test_new() { - let c = Circle::new(1, 2, 4); - assert_eq!(1, c.x); - assert_eq!(2, c.y); - assert_eq!(4, c.radius); - } + #[test] + pub fn test_new() { + let c = Circle::new(1, 2, 4); + assert_eq!(1, c.x); + assert_eq!(2, c.y); + assert_eq!(4, c.radius); + } - #[test] - pub fn test_diameter() { - let c = Circle::new(4, 4, 3); - assert_eq!(6, c.diameter()); - } + #[test] + pub fn test_diameter() { + let c = Circle::new(4, 4, 3); + assert_eq!(6, c.diameter()); + } - #[test] - pub fn test_contains_point() { - let c = Circle::new(1, 1, 6); - assert!(c.contains_point(4, 4)); - assert!(!c.contains_point(8, 4)); + #[test] + pub fn test_contains_point() { + let c = Circle::new(1, 1, 6); + assert!(c.contains_point(4, 4)); + assert!(!c.contains_point(8, 4)); - let c = Circle::new(0, 1, 2); - assert!(!c.contains_point(3, 3)); - assert!(c.contains_point(0, 0)); - } + let c = Circle::new(0, 1, 2); + assert!(!c.contains_point(3, 3)); + assert!(c.contains_point(0, 0)); + } - #[test] - pub fn test_overlaps() { - let a = Circle::new(3, 4, 5); - let b = Circle::new(14, 18, 8); - assert!(!a.overlaps(&b)); + #[test] + pub fn test_overlaps() { + let a = Circle::new(3, 4, 5); + let b = Circle::new(14, 18, 8); + assert!(!a.overlaps(&b)); - let a = Circle::new(2, 3, 12); - let b = Circle::new(15, 28, 10); - assert!(!a.overlaps(&b)); + let a = Circle::new(2, 3, 12); + let b = Circle::new(15, 28, 10); + assert!(!a.overlaps(&b)); - let a = Circle::new(-10, 8, 30); - let b = Circle::new(14, -24, 10); - assert!(a.overlaps(&b)); - } + let a = Circle::new(-10, 8, 30); + let b = Circle::new(14, -24, 10); + assert!(a.overlaps(&b)); + } } diff --git a/libretrogd/src/math/matrix3x3.rs b/libretrogd/src/math/matrix3x3.rs index 802aa67..2ad0ec6 100644 --- a/libretrogd/src/math/matrix3x3.rs +++ b/libretrogd/src/math/matrix3x3.rs @@ -5,414 +5,414 @@ use crate::math::*; /// Represents a 3x3 column-major matrix and provides common methods for matrix math. #[derive(Debug, Copy, Clone, PartialEq)] pub struct Matrix3x3 { - pub m: [f32; 9], + pub m: [f32; 9], } impl Matrix3x3 { - pub const M11: usize = 0; - pub const M12: usize = 3; - pub const M13: usize = 6; - pub const M21: usize = 1; - pub const M22: usize = 4; - pub const M23: usize = 7; - pub const M31: usize = 2; - pub const M32: usize = 5; - pub const M33: usize = 8; + pub const M11: usize = 0; + pub const M12: usize = 3; + pub const M13: usize = 6; + pub const M21: usize = 1; + pub const M22: usize = 4; + pub const M23: usize = 7; + pub const M31: usize = 2; + pub const M32: usize = 5; + pub const M33: usize = 8; - pub const IDENTITY: Matrix3x3 = Matrix3x3 { - m: [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0], - }; + pub const IDENTITY: Matrix3x3 = Matrix3x3 { + m: [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0], + }; - /// Returns a new identity matrix. - #[inline] - pub fn identity() -> Matrix3x3 { - Matrix3x3::IDENTITY - } + /// Returns a new identity matrix. + #[inline] + pub fn identity() -> Matrix3x3 { + Matrix3x3::IDENTITY + } - /// Creates a new matrix with the specified elements. - #[rustfmt::skip] - #[inline] - pub fn new( - m11: f32, m12: f32, m13: f32, - m21: f32, m22: f32, m23: f32, - m31: f32, m32: f32, m33: f32, - ) -> Matrix3x3 { - Matrix3x3 { - m: [ - m11, m21, m31, - m12, m22, m32, - m13, m23, m33 - ], - } - } + /// Creates a new matrix with the specified elements. + #[rustfmt::skip] + #[inline] + pub fn new( + m11: f32, m12: f32, m13: f32, + m21: f32, m22: f32, m23: f32, + m31: f32, m32: f32, m33: f32, + ) -> Matrix3x3 { + Matrix3x3 { + m: [ + m11, m21, m31, + m12, m22, m32, + m13, m23, m33 + ], + } + } - /// Creates a new rotation matrix from a set of euler angles. - /// - /// # Arguments - /// - /// * `x`: the x angle (in radians) - /// * `y`: the y angle (in radians) - /// * `z`: the z angle (in radians) - pub fn from_euler_angles(x: f32, y: f32, z: f32) -> Matrix3x3 { - let rotate_z = Matrix3x3::new_rotation_z(z); - let rotate_y = Matrix3x3::new_rotation_y(y); - let rotate_x = Matrix3x3::new_rotation_x(x); + /// Creates a new rotation matrix from a set of euler angles. + /// + /// # Arguments + /// + /// * `x`: the x angle (in radians) + /// * `y`: the y angle (in radians) + /// * `z`: the z angle (in radians) + pub fn from_euler_angles(x: f32, y: f32, z: f32) -> Matrix3x3 { + let rotate_z = Matrix3x3::new_rotation_z(z); + let rotate_y = Matrix3x3::new_rotation_y(y); + let rotate_x = Matrix3x3::new_rotation_x(x); - // "right-to-left" column-major matrix concatenation - rotate_z * rotate_y * rotate_x - } + // "right-to-left" column-major matrix concatenation + rotate_z * rotate_y * rotate_x + } - /// Creates a new rotation matrix for rotation around the x axis. - /// - /// # Arguments - /// - /// * `radians`: angle to rotate the x axis around (in radians) - #[rustfmt::skip] - #[inline] - pub fn new_rotation_x(radians: f32) -> Matrix3x3 { - let (s, c) = radians.sin_cos(); - Matrix3x3::new( - 1.0, 0.0, 0.0, - 0.0, c, -s, - 0.0, s, c - ) - } + /// Creates a new rotation matrix for rotation around the x axis. + /// + /// # Arguments + /// + /// * `radians`: angle to rotate the x axis around (in radians) + #[rustfmt::skip] + #[inline] + pub fn new_rotation_x(radians: f32) -> Matrix3x3 { + let (s, c) = radians.sin_cos(); + Matrix3x3::new( + 1.0, 0.0, 0.0, + 0.0, c, -s, + 0.0, s, c, + ) + } - /// Creates a new rotation matrix for rotation around the y axis. - /// - /// # Arguments - /// - /// * `radians`: angle to rotate the y axis around (in radians) - #[rustfmt::skip] - #[inline] - pub fn new_rotation_y(radians: f32) -> Matrix3x3 { - let (s, c) = radians.sin_cos(); - Matrix3x3::new( - c, 0.0, s, - 0.0, 1.0, 0.0, - -s, 0.0, c - ) - } + /// Creates a new rotation matrix for rotation around the y axis. + /// + /// # Arguments + /// + /// * `radians`: angle to rotate the y axis around (in radians) + #[rustfmt::skip] + #[inline] + pub fn new_rotation_y(radians: f32) -> Matrix3x3 { + let (s, c) = radians.sin_cos(); + Matrix3x3::new( + c, 0.0, s, + 0.0, 1.0, 0.0, + -s, 0.0, c, + ) + } - /// Creates a new rotation matrix for rotation around the z axis. - /// - /// # Arguments - /// - /// * `radians`: angle to rotate the z axis around (in radians) - #[rustfmt::skip] - #[inline] - pub fn new_rotation_z(radians: f32) -> Matrix3x3 { - let (s, c) = radians.sin_cos(); - Matrix3x3::new( - c, -s, 0.0, - s, c, 0.0, - 0.0, 0.0, 1.0 - ) - } + /// Creates a new rotation matrix for rotation around the z axis. + /// + /// # Arguments + /// + /// * `radians`: angle to rotate the z axis around (in radians) + #[rustfmt::skip] + #[inline] + pub fn new_rotation_z(radians: f32) -> Matrix3x3 { + let (s, c) = radians.sin_cos(); + Matrix3x3::new( + c, -s, 0.0, + s, c, 0.0, + 0.0, 0.0, 1.0, + ) + } - /// Creates a translation matrix. For use with 2D coordinates only. - /// - /// # Arguments - /// - /// * `x`: the amount to translate on the x axis - /// * `y`: the amount to translate on the y axis - #[rustfmt::skip] - #[inline] - pub fn new_2d_translation(x: f32, y: f32) -> Matrix3x3 { - Matrix3x3::new( - 1.0, 0.0, 0.0, - 0.0, 1.0, 0.0, - x, y, 1.0 - ) - } + /// Creates a translation matrix. For use with 2D coordinates only. + /// + /// # Arguments + /// + /// * `x`: the amount to translate on the x axis + /// * `y`: the amount to translate on the y axis + #[rustfmt::skip] + #[inline] + pub fn new_2d_translation(x: f32, y: f32) -> Matrix3x3 { + Matrix3x3::new( + 1.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + x, y, 1.0, + ) + } - /// Creates a scaling matrix from scaling factors for each axis. For use with 2D coordinates - /// only. - /// - /// # Arguments - /// - /// * `x`: the scale factor for the x axis - /// * `y`: the scale factor for the y axis - #[rustfmt::skip] - #[inline] - pub fn new_2d_scaling(x: f32, y: f32) -> Matrix3x3 { - Matrix3x3::new( - x, 0.0, 0.0, - 0.0, y, 0.0, - 0.0, 0.0, 1.0 - ) - } + /// Creates a scaling matrix from scaling factors for each axis. For use with 2D coordinates + /// only. + /// + /// # Arguments + /// + /// * `x`: the scale factor for the x axis + /// * `y`: the scale factor for the y axis + #[rustfmt::skip] + #[inline] + pub fn new_2d_scaling(x: f32, y: f32) -> Matrix3x3 { + Matrix3x3::new( + x, 0.0, 0.0, + 0.0, y, 0.0, + 0.0, 0.0, 1.0, + ) + } - /// Creates a new rotation matrix. For use with 2D coordinates only. - /// - /// # Arguments - /// - /// * `radians`: angle to rotate by (in radians) - #[inline(always)] - pub fn new_2d_rotation(radians: f32) -> Matrix3x3 { - Matrix3x3::new_rotation_z(radians) - } + /// Creates a new rotation matrix. For use with 2D coordinates only. + /// + /// # Arguments + /// + /// * `radians`: angle to rotate by (in radians) + #[inline(always)] + pub fn new_2d_rotation(radians: f32) -> Matrix3x3 { + Matrix3x3::new_rotation_z(radians) + } - /// Calculates the determinant of this matrix. - #[rustfmt::skip] - #[inline] - pub fn determinant(&self) -> f32 { - self.m[Matrix3x3::M11] * self.m[Matrix3x3::M22] * self.m[Matrix3x3::M33] + - self.m[Matrix3x3::M12] * self.m[Matrix3x3::M23] * self.m[Matrix3x3::M31] + - self.m[Matrix3x3::M13] * self.m[Matrix3x3::M21] * self.m[Matrix3x3::M32] - - self.m[Matrix3x3::M11] * self.m[Matrix3x3::M23] * self.m[Matrix3x3::M32] - - self.m[Matrix3x3::M12] * self.m[Matrix3x3::M21] * self.m[Matrix3x3::M33] - - self.m[Matrix3x3::M13] * self.m[Matrix3x3::M22] * self.m[Matrix3x3::M31] - } + /// Calculates the determinant of this matrix. + #[rustfmt::skip] + #[inline] + pub fn determinant(&self) -> f32 { + self.m[Matrix3x3::M11] * self.m[Matrix3x3::M22] * self.m[Matrix3x3::M33] + + self.m[Matrix3x3::M12] * self.m[Matrix3x3::M23] * self.m[Matrix3x3::M31] + + self.m[Matrix3x3::M13] * self.m[Matrix3x3::M21] * self.m[Matrix3x3::M32] - + self.m[Matrix3x3::M11] * self.m[Matrix3x3::M23] * self.m[Matrix3x3::M32] - + self.m[Matrix3x3::M12] * self.m[Matrix3x3::M21] * self.m[Matrix3x3::M33] - + self.m[Matrix3x3::M13] * self.m[Matrix3x3::M22] * self.m[Matrix3x3::M31] + } - /// Calculates the inverse of this matrix. - #[rustfmt::skip] - pub fn invert(&self) -> Matrix3x3 { - let d = self.determinant(); - if nearly_equal(d, 0.0, 0.000001) { - Matrix3x3::IDENTITY - } else { - let d = 1.0 / d; - Matrix3x3 { - m: [ - d * (self.m[Matrix3x3::M22] * self.m[Matrix3x3::M33] - self.m[Matrix3x3::M32] * self.m[Matrix3x3::M23]), - d * (self.m[Matrix3x3::M31] * self.m[Matrix3x3::M23] - self.m[Matrix3x3::M21] * self.m[Matrix3x3::M33]), - d * (self.m[Matrix3x3::M21] * self.m[Matrix3x3::M32] - self.m[Matrix3x3::M31] * self.m[Matrix3x3::M22]), - d * (self.m[Matrix3x3::M32] * self.m[Matrix3x3::M13] - self.m[Matrix3x3::M12] * self.m[Matrix3x3::M33]), - d * (self.m[Matrix3x3::M11] * self.m[Matrix3x3::M33] - self.m[Matrix3x3::M31] * self.m[Matrix3x3::M13]), - d * (self.m[Matrix3x3::M31] * self.m[Matrix3x3::M12] - self.m[Matrix3x3::M11] * self.m[Matrix3x3::M32]), - d * (self.m[Matrix3x3::M12] * self.m[Matrix3x3::M23] - self.m[Matrix3x3::M22] * self.m[Matrix3x3::M13]), - d * (self.m[Matrix3x3::M21] * self.m[Matrix3x3::M13] - self.m[Matrix3x3::M11] * self.m[Matrix3x3::M23]), - d * (self.m[Matrix3x3::M11] * self.m[Matrix3x3::M22] - self.m[Matrix3x3::M21] * self.m[Matrix3x3::M12]), - ] - } - } - } + /// Calculates the inverse of this matrix. + #[rustfmt::skip] + pub fn invert(&self) -> Matrix3x3 { + let d = self.determinant(); + if nearly_equal(d, 0.0, 0.000001) { + Matrix3x3::IDENTITY + } else { + let d = 1.0 / d; + Matrix3x3 { + m: [ + d * (self.m[Matrix3x3::M22] * self.m[Matrix3x3::M33] - self.m[Matrix3x3::M32] * self.m[Matrix3x3::M23]), + d * (self.m[Matrix3x3::M31] * self.m[Matrix3x3::M23] - self.m[Matrix3x3::M21] * self.m[Matrix3x3::M33]), + d * (self.m[Matrix3x3::M21] * self.m[Matrix3x3::M32] - self.m[Matrix3x3::M31] * self.m[Matrix3x3::M22]), + d * (self.m[Matrix3x3::M32] * self.m[Matrix3x3::M13] - self.m[Matrix3x3::M12] * self.m[Matrix3x3::M33]), + d * (self.m[Matrix3x3::M11] * self.m[Matrix3x3::M33] - self.m[Matrix3x3::M31] * self.m[Matrix3x3::M13]), + d * (self.m[Matrix3x3::M31] * self.m[Matrix3x3::M12] - self.m[Matrix3x3::M11] * self.m[Matrix3x3::M32]), + d * (self.m[Matrix3x3::M12] * self.m[Matrix3x3::M23] - self.m[Matrix3x3::M22] * self.m[Matrix3x3::M13]), + d * (self.m[Matrix3x3::M21] * self.m[Matrix3x3::M13] - self.m[Matrix3x3::M11] * self.m[Matrix3x3::M23]), + d * (self.m[Matrix3x3::M11] * self.m[Matrix3x3::M22] - self.m[Matrix3x3::M21] * self.m[Matrix3x3::M12]), + ] + } + } + } - /// Calculates the transpose of this matrix. - #[inline] - pub fn transpose(&self) -> Matrix3x3 { - Matrix3x3::new( - self.m[Matrix3x3::M11], - self.m[Matrix3x3::M21], - self.m[Matrix3x3::M31], - self.m[Matrix3x3::M12], - self.m[Matrix3x3::M22], - self.m[Matrix3x3::M32], - self.m[Matrix3x3::M13], - self.m[Matrix3x3::M23], - self.m[Matrix3x3::M33], - ) - } + /// Calculates the transpose of this matrix. + #[inline] + pub fn transpose(&self) -> Matrix3x3 { + Matrix3x3::new( + self.m[Matrix3x3::M11], + self.m[Matrix3x3::M21], + self.m[Matrix3x3::M31], + self.m[Matrix3x3::M12], + self.m[Matrix3x3::M22], + self.m[Matrix3x3::M32], + self.m[Matrix3x3::M13], + self.m[Matrix3x3::M23], + self.m[Matrix3x3::M33], + ) + } - /// Sets all of the elements of this matrix. - #[inline] - pub fn set( - &mut self, - m11: f32, - m12: f32, - m13: f32, - m21: f32, - m22: f32, - m23: f32, - m31: f32, - m32: f32, - m33: f32, - ) { - self.m[Matrix3x3::M11] = m11; - self.m[Matrix3x3::M12] = m12; - self.m[Matrix3x3::M13] = m13; - self.m[Matrix3x3::M21] = m21; - self.m[Matrix3x3::M22] = m22; - self.m[Matrix3x3::M23] = m23; - self.m[Matrix3x3::M31] = m31; - self.m[Matrix3x3::M32] = m32; - self.m[Matrix3x3::M33] = m33; - } + /// Sets all of the elements of this matrix. + #[inline] + pub fn set( + &mut self, + m11: f32, + m12: f32, + m13: f32, + m21: f32, + m22: f32, + m23: f32, + m31: f32, + m32: f32, + m33: f32, + ) { + self.m[Matrix3x3::M11] = m11; + self.m[Matrix3x3::M12] = m12; + self.m[Matrix3x3::M13] = m13; + self.m[Matrix3x3::M21] = m21; + self.m[Matrix3x3::M22] = m22; + self.m[Matrix3x3::M23] = m23; + self.m[Matrix3x3::M31] = m31; + self.m[Matrix3x3::M32] = m32; + self.m[Matrix3x3::M33] = m33; + } } impl Mul for Matrix3x3 { - type Output = Self; + type Output = Self; - #[rustfmt::skip] - #[inline] - fn mul(self, rhs: Self) -> Self::Output { - Matrix3x3::new( - self.m[Matrix3x3::M11] * rhs.m[Matrix3x3::M11] + self.m[Matrix3x3::M12] * rhs.m[Matrix3x3::M21] + self.m[Matrix3x3::M13] * rhs.m[Matrix3x3::M31], - self.m[Matrix3x3::M11] * rhs.m[Matrix3x3::M12] + self.m[Matrix3x3::M12] * rhs.m[Matrix3x3::M22] + self.m[Matrix3x3::M13] * rhs.m[Matrix3x3::M32], - self.m[Matrix3x3::M11] * rhs.m[Matrix3x3::M13] + self.m[Matrix3x3::M12] * rhs.m[Matrix3x3::M23] + self.m[Matrix3x3::M13] * rhs.m[Matrix3x3::M33], - self.m[Matrix3x3::M21] * rhs.m[Matrix3x3::M11] + self.m[Matrix3x3::M22] * rhs.m[Matrix3x3::M21] + self.m[Matrix3x3::M23] * rhs.m[Matrix3x3::M31], - self.m[Matrix3x3::M21] * rhs.m[Matrix3x3::M12] + self.m[Matrix3x3::M22] * rhs.m[Matrix3x3::M22] + self.m[Matrix3x3::M23] * rhs.m[Matrix3x3::M32], - self.m[Matrix3x3::M21] * rhs.m[Matrix3x3::M13] + self.m[Matrix3x3::M22] * rhs.m[Matrix3x3::M23] + self.m[Matrix3x3::M23] * rhs.m[Matrix3x3::M33], - self.m[Matrix3x3::M31] * rhs.m[Matrix3x3::M11] + self.m[Matrix3x3::M32] * rhs.m[Matrix3x3::M21] + self.m[Matrix3x3::M33] * rhs.m[Matrix3x3::M31], - self.m[Matrix3x3::M31] * rhs.m[Matrix3x3::M12] + self.m[Matrix3x3::M32] * rhs.m[Matrix3x3::M22] + self.m[Matrix3x3::M33] * rhs.m[Matrix3x3::M32], - self.m[Matrix3x3::M31] * rhs.m[Matrix3x3::M13] + self.m[Matrix3x3::M32] * rhs.m[Matrix3x3::M23] + self.m[Matrix3x3::M33] * rhs.m[Matrix3x3::M33] - ) - } + #[rustfmt::skip] + #[inline] + fn mul(self, rhs: Self) -> Self::Output { + Matrix3x3::new( + self.m[Matrix3x3::M11] * rhs.m[Matrix3x3::M11] + self.m[Matrix3x3::M12] * rhs.m[Matrix3x3::M21] + self.m[Matrix3x3::M13] * rhs.m[Matrix3x3::M31], + self.m[Matrix3x3::M11] * rhs.m[Matrix3x3::M12] + self.m[Matrix3x3::M12] * rhs.m[Matrix3x3::M22] + self.m[Matrix3x3::M13] * rhs.m[Matrix3x3::M32], + self.m[Matrix3x3::M11] * rhs.m[Matrix3x3::M13] + self.m[Matrix3x3::M12] * rhs.m[Matrix3x3::M23] + self.m[Matrix3x3::M13] * rhs.m[Matrix3x3::M33], + self.m[Matrix3x3::M21] * rhs.m[Matrix3x3::M11] + self.m[Matrix3x3::M22] * rhs.m[Matrix3x3::M21] + self.m[Matrix3x3::M23] * rhs.m[Matrix3x3::M31], + self.m[Matrix3x3::M21] * rhs.m[Matrix3x3::M12] + self.m[Matrix3x3::M22] * rhs.m[Matrix3x3::M22] + self.m[Matrix3x3::M23] * rhs.m[Matrix3x3::M32], + self.m[Matrix3x3::M21] * rhs.m[Matrix3x3::M13] + self.m[Matrix3x3::M22] * rhs.m[Matrix3x3::M23] + self.m[Matrix3x3::M23] * rhs.m[Matrix3x3::M33], + self.m[Matrix3x3::M31] * rhs.m[Matrix3x3::M11] + self.m[Matrix3x3::M32] * rhs.m[Matrix3x3::M21] + self.m[Matrix3x3::M33] * rhs.m[Matrix3x3::M31], + self.m[Matrix3x3::M31] * rhs.m[Matrix3x3::M12] + self.m[Matrix3x3::M32] * rhs.m[Matrix3x3::M22] + self.m[Matrix3x3::M33] * rhs.m[Matrix3x3::M32], + self.m[Matrix3x3::M31] * rhs.m[Matrix3x3::M13] + self.m[Matrix3x3::M32] * rhs.m[Matrix3x3::M23] + self.m[Matrix3x3::M33] * rhs.m[Matrix3x3::M33], + ) + } } impl MulAssign for Matrix3x3 { - #[rustfmt::skip] - #[inline] - fn mul_assign(&mut self, rhs: Self) { - self.set( - self.m[Matrix3x3::M11] * rhs.m[Matrix3x3::M11] + self.m[Matrix3x3::M12] * rhs.m[Matrix3x3::M21] + self.m[Matrix3x3::M13] * rhs.m[Matrix3x3::M31], - self.m[Matrix3x3::M11] * rhs.m[Matrix3x3::M12] + self.m[Matrix3x3::M12] * rhs.m[Matrix3x3::M22] + self.m[Matrix3x3::M13] * rhs.m[Matrix3x3::M32], - self.m[Matrix3x3::M11] * rhs.m[Matrix3x3::M13] + self.m[Matrix3x3::M12] * rhs.m[Matrix3x3::M23] + self.m[Matrix3x3::M13] * rhs.m[Matrix3x3::M33], - self.m[Matrix3x3::M21] * rhs.m[Matrix3x3::M11] + self.m[Matrix3x3::M22] * rhs.m[Matrix3x3::M21] + self.m[Matrix3x3::M23] * rhs.m[Matrix3x3::M31], - self.m[Matrix3x3::M21] * rhs.m[Matrix3x3::M12] + self.m[Matrix3x3::M22] * rhs.m[Matrix3x3::M22] + self.m[Matrix3x3::M23] * rhs.m[Matrix3x3::M32], - self.m[Matrix3x3::M21] * rhs.m[Matrix3x3::M13] + self.m[Matrix3x3::M22] * rhs.m[Matrix3x3::M23] + self.m[Matrix3x3::M23] * rhs.m[Matrix3x3::M33], - self.m[Matrix3x3::M31] * rhs.m[Matrix3x3::M11] + self.m[Matrix3x3::M32] * rhs.m[Matrix3x3::M21] + self.m[Matrix3x3::M33] * rhs.m[Matrix3x3::M31], - self.m[Matrix3x3::M31] * rhs.m[Matrix3x3::M12] + self.m[Matrix3x3::M32] * rhs.m[Matrix3x3::M22] + self.m[Matrix3x3::M33] * rhs.m[Matrix3x3::M32], - self.m[Matrix3x3::M31] * rhs.m[Matrix3x3::M13] + self.m[Matrix3x3::M32] * rhs.m[Matrix3x3::M23] + self.m[Matrix3x3::M33] * rhs.m[Matrix3x3::M33] - ) - } + #[rustfmt::skip] + #[inline] + fn mul_assign(&mut self, rhs: Self) { + self.set( + self.m[Matrix3x3::M11] * rhs.m[Matrix3x3::M11] + self.m[Matrix3x3::M12] * rhs.m[Matrix3x3::M21] + self.m[Matrix3x3::M13] * rhs.m[Matrix3x3::M31], + self.m[Matrix3x3::M11] * rhs.m[Matrix3x3::M12] + self.m[Matrix3x3::M12] * rhs.m[Matrix3x3::M22] + self.m[Matrix3x3::M13] * rhs.m[Matrix3x3::M32], + self.m[Matrix3x3::M11] * rhs.m[Matrix3x3::M13] + self.m[Matrix3x3::M12] * rhs.m[Matrix3x3::M23] + self.m[Matrix3x3::M13] * rhs.m[Matrix3x3::M33], + self.m[Matrix3x3::M21] * rhs.m[Matrix3x3::M11] + self.m[Matrix3x3::M22] * rhs.m[Matrix3x3::M21] + self.m[Matrix3x3::M23] * rhs.m[Matrix3x3::M31], + self.m[Matrix3x3::M21] * rhs.m[Matrix3x3::M12] + self.m[Matrix3x3::M22] * rhs.m[Matrix3x3::M22] + self.m[Matrix3x3::M23] * rhs.m[Matrix3x3::M32], + self.m[Matrix3x3::M21] * rhs.m[Matrix3x3::M13] + self.m[Matrix3x3::M22] * rhs.m[Matrix3x3::M23] + self.m[Matrix3x3::M23] * rhs.m[Matrix3x3::M33], + self.m[Matrix3x3::M31] * rhs.m[Matrix3x3::M11] + self.m[Matrix3x3::M32] * rhs.m[Matrix3x3::M21] + self.m[Matrix3x3::M33] * rhs.m[Matrix3x3::M31], + self.m[Matrix3x3::M31] * rhs.m[Matrix3x3::M12] + self.m[Matrix3x3::M32] * rhs.m[Matrix3x3::M22] + self.m[Matrix3x3::M33] * rhs.m[Matrix3x3::M32], + self.m[Matrix3x3::M31] * rhs.m[Matrix3x3::M13] + self.m[Matrix3x3::M32] * rhs.m[Matrix3x3::M23] + self.m[Matrix3x3::M33] * rhs.m[Matrix3x3::M33], + ) + } } impl Mul for Matrix3x3 { - type Output = Vector2; + type Output = Vector2; - #[rustfmt::skip] - #[inline] - fn mul(self, rhs: Vector2) -> Self::Output { - Vector2 { - x: rhs.x * self.m[Matrix3x3::M11] + rhs.y * self.m[Matrix3x3::M12] + self.m[Matrix3x3::M13] + self.m[Matrix3x3::M31], - y: rhs.x * self.m[Matrix3x3::M21] + rhs.y * self.m[Matrix3x3::M22] + self.m[Matrix3x3::M23] + self.m[Matrix3x3::M32] - } - } + #[rustfmt::skip] + #[inline] + fn mul(self, rhs: Vector2) -> Self::Output { + Vector2 { + x: rhs.x * self.m[Matrix3x3::M11] + rhs.y * self.m[Matrix3x3::M12] + self.m[Matrix3x3::M13] + self.m[Matrix3x3::M31], + y: rhs.x * self.m[Matrix3x3::M21] + rhs.y * self.m[Matrix3x3::M22] + self.m[Matrix3x3::M23] + self.m[Matrix3x3::M32], + } + } } #[cfg(test)] pub mod tests { - use super::*; + use super::*; - #[test] - pub fn test_new() { - let m = Matrix3x3::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0); - assert_eq!(1.0, m.m[Matrix3x3::M11]); - assert_eq!(2.0, m.m[Matrix3x3::M12]); - assert_eq!(3.0, m.m[Matrix3x3::M13]); - assert_eq!(4.0, m.m[Matrix3x3::M21]); - assert_eq!(5.0, m.m[Matrix3x3::M22]); - assert_eq!(6.0, m.m[Matrix3x3::M23]); - assert_eq!(7.0, m.m[Matrix3x3::M31]); - assert_eq!(8.0, m.m[Matrix3x3::M32]); - assert_eq!(9.0, m.m[Matrix3x3::M33]); - } + #[test] + pub fn test_new() { + let m = Matrix3x3::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0); + assert_eq!(1.0, m.m[Matrix3x3::M11]); + assert_eq!(2.0, m.m[Matrix3x3::M12]); + assert_eq!(3.0, m.m[Matrix3x3::M13]); + assert_eq!(4.0, m.m[Matrix3x3::M21]); + assert_eq!(5.0, m.m[Matrix3x3::M22]); + assert_eq!(6.0, m.m[Matrix3x3::M23]); + assert_eq!(7.0, m.m[Matrix3x3::M31]); + assert_eq!(8.0, m.m[Matrix3x3::M32]); + assert_eq!(9.0, m.m[Matrix3x3::M33]); + } - #[test] - pub fn test_set() { - let mut m = Matrix3x3 { m: [0.0; 9] }; - m.set(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0); - assert_eq!(1.0, m.m[Matrix3x3::M11]); - assert_eq!(2.0, m.m[Matrix3x3::M12]); - assert_eq!(3.0, m.m[Matrix3x3::M13]); - assert_eq!(4.0, m.m[Matrix3x3::M21]); - assert_eq!(5.0, m.m[Matrix3x3::M22]); - assert_eq!(6.0, m.m[Matrix3x3::M23]); - assert_eq!(7.0, m.m[Matrix3x3::M31]); - assert_eq!(8.0, m.m[Matrix3x3::M32]); - assert_eq!(9.0, m.m[Matrix3x3::M33]); - } + #[test] + pub fn test_set() { + let mut m = Matrix3x3 { m: [0.0; 9] }; + m.set(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0); + assert_eq!(1.0, m.m[Matrix3x3::M11]); + assert_eq!(2.0, m.m[Matrix3x3::M12]); + assert_eq!(3.0, m.m[Matrix3x3::M13]); + assert_eq!(4.0, m.m[Matrix3x3::M21]); + assert_eq!(5.0, m.m[Matrix3x3::M22]); + assert_eq!(6.0, m.m[Matrix3x3::M23]); + assert_eq!(7.0, m.m[Matrix3x3::M31]); + assert_eq!(8.0, m.m[Matrix3x3::M32]); + assert_eq!(9.0, m.m[Matrix3x3::M33]); + } - #[test] - pub fn test_identity() { - let m = Matrix3x3::identity(); - assert_eq!(1.0, m.m[Matrix3x3::M11]); - assert_eq!(0.0, m.m[Matrix3x3::M12]); - assert_eq!(0.0, m.m[Matrix3x3::M13]); - assert_eq!(0.0, m.m[Matrix3x3::M21]); - assert_eq!(1.0, m.m[Matrix3x3::M22]); - assert_eq!(0.0, m.m[Matrix3x3::M23]); - assert_eq!(0.0, m.m[Matrix3x3::M31]); - assert_eq!(0.0, m.m[Matrix3x3::M32]); - assert_eq!(1.0, m.m[Matrix3x3::M33]); - } + #[test] + pub fn test_identity() { + let m = Matrix3x3::identity(); + assert_eq!(1.0, m.m[Matrix3x3::M11]); + assert_eq!(0.0, m.m[Matrix3x3::M12]); + assert_eq!(0.0, m.m[Matrix3x3::M13]); + assert_eq!(0.0, m.m[Matrix3x3::M21]); + assert_eq!(1.0, m.m[Matrix3x3::M22]); + assert_eq!(0.0, m.m[Matrix3x3::M23]); + assert_eq!(0.0, m.m[Matrix3x3::M31]); + assert_eq!(0.0, m.m[Matrix3x3::M32]); + assert_eq!(1.0, m.m[Matrix3x3::M33]); + } - #[rustfmt::skip] - #[test] - pub fn test_transpose() { - let m = Matrix3x3::new( - 1.0, 2.0, 3.0, - 4.0, 5.0, 6.0, - 7.0, 8.0, 9.0 - ); - let t = m.transpose(); - assert_eq!(1.0, t.m[Matrix3x3::M11]); - assert_eq!(4.0, t.m[Matrix3x3::M12]); - assert_eq!(7.0, t.m[Matrix3x3::M13]); - assert_eq!(2.0, t.m[Matrix3x3::M21]); - assert_eq!(5.0, t.m[Matrix3x3::M22]); - assert_eq!(8.0, t.m[Matrix3x3::M23]); - assert_eq!(3.0, t.m[Matrix3x3::M31]); - assert_eq!(6.0, t.m[Matrix3x3::M32]); - assert_eq!(9.0, t.m[Matrix3x3::M33]); - } + #[rustfmt::skip] + #[test] + pub fn test_transpose() { + let m = Matrix3x3::new( + 1.0, 2.0, 3.0, + 4.0, 5.0, 6.0, + 7.0, 8.0, 9.0, + ); + let t = m.transpose(); + assert_eq!(1.0, t.m[Matrix3x3::M11]); + assert_eq!(4.0, t.m[Matrix3x3::M12]); + assert_eq!(7.0, t.m[Matrix3x3::M13]); + assert_eq!(2.0, t.m[Matrix3x3::M21]); + assert_eq!(5.0, t.m[Matrix3x3::M22]); + assert_eq!(8.0, t.m[Matrix3x3::M23]); + assert_eq!(3.0, t.m[Matrix3x3::M31]); + assert_eq!(6.0, t.m[Matrix3x3::M32]); + assert_eq!(9.0, t.m[Matrix3x3::M33]); + } - #[test] - pub fn test_mul() { - let a = Matrix3x3::new(12.0, 8.0, 4.0, 3.0, 17.0, 14.0, 9.0, 8.0, 10.0); - let b = Matrix3x3::new(5.0, 19.0, 3.0, 6.0, 15.0, 9.0, 7.0, 8.0, 16.0); - let c = a * b; - assert!(nearly_equal(136.0, c.m[Matrix3x3::M11], 0.001)); - assert!(nearly_equal(380.0, c.m[Matrix3x3::M12], 0.001)); - assert!(nearly_equal(172.0, c.m[Matrix3x3::M13], 0.001)); - assert!(nearly_equal(215.0, c.m[Matrix3x3::M21], 0.001)); - assert!(nearly_equal(424.0, c.m[Matrix3x3::M22], 0.001)); - assert!(nearly_equal(386.0, c.m[Matrix3x3::M23], 0.001)); - assert!(nearly_equal(163.0, c.m[Matrix3x3::M31], 0.001)); - assert!(nearly_equal(371.0, c.m[Matrix3x3::M32], 0.001)); - assert!(nearly_equal(259.0, c.m[Matrix3x3::M33], 0.001)); + #[test] + pub fn test_mul() { + let a = Matrix3x3::new(12.0, 8.0, 4.0, 3.0, 17.0, 14.0, 9.0, 8.0, 10.0); + let b = Matrix3x3::new(5.0, 19.0, 3.0, 6.0, 15.0, 9.0, 7.0, 8.0, 16.0); + let c = a * b; + assert!(nearly_equal(136.0, c.m[Matrix3x3::M11], 0.001)); + assert!(nearly_equal(380.0, c.m[Matrix3x3::M12], 0.001)); + assert!(nearly_equal(172.0, c.m[Matrix3x3::M13], 0.001)); + assert!(nearly_equal(215.0, c.m[Matrix3x3::M21], 0.001)); + assert!(nearly_equal(424.0, c.m[Matrix3x3::M22], 0.001)); + assert!(nearly_equal(386.0, c.m[Matrix3x3::M23], 0.001)); + assert!(nearly_equal(163.0, c.m[Matrix3x3::M31], 0.001)); + assert!(nearly_equal(371.0, c.m[Matrix3x3::M32], 0.001)); + assert!(nearly_equal(259.0, c.m[Matrix3x3::M33], 0.001)); - let mut a = Matrix3x3::new(12.0, 8.0, 4.0, 3.0, 17.0, 14.0, 9.0, 8.0, 10.0); - let b = Matrix3x3::new(5.0, 19.0, 3.0, 6.0, 15.0, 9.0, 7.0, 8.0, 16.0); - a *= b; - assert!(nearly_equal(136.0, a.m[Matrix3x3::M11], 0.001)); - assert!(nearly_equal(380.0, a.m[Matrix3x3::M12], 0.001)); - assert!(nearly_equal(172.0, a.m[Matrix3x3::M13], 0.001)); - assert!(nearly_equal(215.0, a.m[Matrix3x3::M21], 0.001)); - assert!(nearly_equal(424.0, a.m[Matrix3x3::M22], 0.001)); - assert!(nearly_equal(386.0, a.m[Matrix3x3::M23], 0.001)); - assert!(nearly_equal(163.0, a.m[Matrix3x3::M31], 0.001)); - assert!(nearly_equal(371.0, a.m[Matrix3x3::M32], 0.001)); - assert!(nearly_equal(259.0, a.m[Matrix3x3::M33], 0.001)); - } + let mut a = Matrix3x3::new(12.0, 8.0, 4.0, 3.0, 17.0, 14.0, 9.0, 8.0, 10.0); + let b = Matrix3x3::new(5.0, 19.0, 3.0, 6.0, 15.0, 9.0, 7.0, 8.0, 16.0); + a *= b; + assert!(nearly_equal(136.0, a.m[Matrix3x3::M11], 0.001)); + assert!(nearly_equal(380.0, a.m[Matrix3x3::M12], 0.001)); + assert!(nearly_equal(172.0, a.m[Matrix3x3::M13], 0.001)); + assert!(nearly_equal(215.0, a.m[Matrix3x3::M21], 0.001)); + assert!(nearly_equal(424.0, a.m[Matrix3x3::M22], 0.001)); + assert!(nearly_equal(386.0, a.m[Matrix3x3::M23], 0.001)); + assert!(nearly_equal(163.0, a.m[Matrix3x3::M31], 0.001)); + assert!(nearly_equal(371.0, a.m[Matrix3x3::M32], 0.001)); + assert!(nearly_equal(259.0, a.m[Matrix3x3::M33], 0.001)); + } - #[test] - pub fn test_2d_translation() { - let v = Vector2::new(10.2, 5.7); - let m = Matrix3x3::new_2d_translation(2.0, 3.0); - let t = m * v; - assert!(nearly_equal(12.2, t.x, 0.001)); - assert!(nearly_equal(8.7, t.y, 0.001)); - } + #[test] + pub fn test_2d_translation() { + let v = Vector2::new(10.2, 5.7); + let m = Matrix3x3::new_2d_translation(2.0, 3.0); + let t = m * v; + assert!(nearly_equal(12.2, t.x, 0.001)); + assert!(nearly_equal(8.7, t.y, 0.001)); + } - #[test] - pub fn test_2d_scaling() { - let v = Vector2::new(10.2, 5.7); - let m = Matrix3x3::new_2d_scaling(3.0, 4.0); - let t = m * v; - assert!(nearly_equal(30.6, t.x, 0.001)); - assert!(nearly_equal(22.8, t.y, 0.001)); - } + #[test] + pub fn test_2d_scaling() { + let v = Vector2::new(10.2, 5.7); + let m = Matrix3x3::new_2d_scaling(3.0, 4.0); + let t = m * v; + assert!(nearly_equal(30.6, t.x, 0.001)); + assert!(nearly_equal(22.8, t.y, 0.001)); + } - #[test] - pub fn test_2d_rotation() { - let v = Vector2::new(0.0, 5.0); - let m = Matrix3x3::new_2d_rotation(RADIANS_90); - let t = m * v; - assert!(nearly_equal(-5.0, t.x, 0.001)); - assert!(nearly_equal(0.0, t.y, 0.001)); - } + #[test] + pub fn test_2d_rotation() { + let v = Vector2::new(0.0, 5.0); + let m = Matrix3x3::new_2d_rotation(RADIANS_90); + let t = m * v; + assert!(nearly_equal(-5.0, t.x, 0.001)); + assert!(nearly_equal(0.0, t.y, 0.001)); + } - #[test] - pub fn test_2d_combined_transform() { - let a = Matrix3x3::new_2d_translation(-2.0, 0.0); - let b = Matrix3x3::new_2d_rotation(RADIANS_180); - let m = a * b; - let v = Vector2::new(0.0, 5.0); - let t = m * v; - assert!(nearly_equal(2.0, t.x, 0.001)); - assert!(nearly_equal(-5.0, t.y, 0.001)); - } + #[test] + pub fn test_2d_combined_transform() { + let a = Matrix3x3::new_2d_translation(-2.0, 0.0); + let b = Matrix3x3::new_2d_rotation(RADIANS_180); + let m = a * b; + let v = Vector2::new(0.0, 5.0); + let t = m * v; + assert!(nearly_equal(2.0, t.x, 0.001)); + assert!(nearly_equal(-5.0, t.y, 0.001)); + } } diff --git a/libretrogd/src/math/mod.rs b/libretrogd/src/math/mod.rs index 22665ae..eb0b438 100644 --- a/libretrogd/src/math/mod.rs +++ b/libretrogd/src/math/mod.rs @@ -10,9 +10,12 @@ pub mod matrix3x3; pub mod rect; pub mod vector2; -pub const PI: f32 = std::f32::consts::PI; // 180 degrees -pub const HALF_PI: f32 = PI / 2.0; // 90 degrees -pub const QUARTER_PI: f32 = PI / 4.0; // 45 degrees +pub const PI: f32 = std::f32::consts::PI; +// 180 degrees +pub const HALF_PI: f32 = PI / 2.0; +// 90 degrees +pub const QUARTER_PI: f32 = PI / 4.0; +// 45 degrees pub const TWO_PI: f32 = PI * 2.0; // 360 degrees pub const RADIANS_0: f32 = 0.0; @@ -40,8 +43,8 @@ pub const RIGHT: f32 = RADIANS_0; /// precision of the provided epsilon value. #[inline] pub fn nearly_equal(a: f32, b: f32, epsilon: f32) -> bool { - // HACK/TODO: this is a shitty way to do this. but it's "good enough" for me ... - (a - b).abs() <= epsilon + // HACK/TODO: this is a shitty way to do this. but it's "good enough" for me ... + (a - b).abs() <= epsilon } /// Linearly interpolates between two values. @@ -53,10 +56,10 @@ pub fn nearly_equal(a: f32, b: f32, epsilon: f32) -> bool { /// * `t`: the amount to interpolate between the two values, specified as a fraction #[inline] pub fn lerp(a: N, b: N, t: f32) -> N -where - N: Copy + Add + Sub + Mul, + where + N: Copy + Add + Sub + Mul, { - a + (b - a) * t + a + (b - a) * t } /// Given a linearly interpolated value and the original range (high and low) of the linear @@ -69,10 +72,10 @@ where /// * `lerp_result`: the interpolated value between the range given #[inline] pub fn inverse_lerp(a: N, b: N, lerp_result: N) -> f32 -where - N: Copy + Sub + Div, + where + N: Copy + Sub + Div, { - (lerp_result - a) / (b - a) + (lerp_result - a) / (b - a) } /// Interpolates between two values using a cubic equation. @@ -84,11 +87,11 @@ where /// * `t`: the amount to interpolate between the two values, specified as a fraction #[inline] pub fn smooth_lerp(a: N, b: N, t: f32) -> N -where - N: Copy + Add + Sub + Mul, + where + N: Copy + Add + Sub + Mul, { - let t = t.clamp(0.0, 1.0); - lerp(a, b, (t * t) * (3.0 - (2.0 * t))) + let t = t.clamp(0.0, 1.0); + lerp(a, b, (t * t) * (3.0 - (2.0 * t))) } /// Re-scales a given value from an old min/max range to a new and different min/max range such @@ -104,174 +107,174 @@ where /// * `new_max`: new max value (high end of range) #[inline] pub fn scale_range(value: N, old_min: N, old_max: N, new_min: N, new_max: N) -> N -where - N: Copy + Add + Sub + Mul + Div, + where + N: Copy + Add + Sub + Mul + Div, { - (new_max - new_min) * (value - old_min) / (old_max - old_min) + new_min + (new_max - new_min) * (value - old_min) / (old_max - old_min) + new_min } /// Calculates the angle (in radians) between the two points. #[inline] pub fn angle_between(x1: f32, y1: f32, x2: f32, y2: f32) -> f32 { - let delta_x = x2 - x1; - let delta_y = y2 - y1; - delta_y.atan2(delta_x) + let delta_x = x2 - x1; + let delta_y = y2 - y1; + delta_y.atan2(delta_x) } /// Returns the X and Y point of a normalized 2D vector that points in the same direction as /// the given angle. #[inline] pub fn angle_to_direction(radians: f32) -> (f32, f32) { - let x = radians.cos(); - let y = radians.sin(); - (x, y) + let x = radians.cos(); + let y = radians.sin(); + (x, y) } /// Calculates the squared distance between two 2D points. #[inline] pub fn distance_squared_between(x1: f32, y1: f32, x2: f32, y2: f32) -> f32 { - (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1) + (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1) } /// Calculates the distance between two 2D points. #[inline] pub fn distance_between(x1: f32, y1: f32, x2: f32, y2: f32) -> f32 { - distance_squared_between(x1, y1, x2, y2).sqrt() + distance_squared_between(x1, y1, x2, y2).sqrt() } pub trait NearlyEqual { - type Output; + type Output; - /// Returns true if the two f32 values are "close enough" to be considered equal using the - /// precision of the provided epsilon value. - fn nearly_equal(self, other: Self::Output, epsilon: f32) -> bool; + /// Returns true if the two f32 values are "close enough" to be considered equal using the + /// precision of the provided epsilon value. + fn nearly_equal(self, other: Self::Output, epsilon: f32) -> bool; } impl NearlyEqual for f32 { - type Output = f32; + type Output = f32; - #[inline(always)] - fn nearly_equal(self, other: Self::Output, epsilon: f32) -> bool { - nearly_equal(self, other, epsilon) - } + #[inline(always)] + fn nearly_equal(self, other: Self::Output, epsilon: f32) -> bool { + nearly_equal(self, other, epsilon) + } } pub trait WrappingRadians { - type Type; + type Type; - /// Adds two angle values in radians together returning the result. The addition will wrap - /// around so that the returned result always lies within 0 -> 2Ï€ radians (0 -> 360 degrees). - fn wrapping_radians_add(self, other: Self::Type) -> Self::Type; + /// Adds two angle values in radians together returning the result. The addition will wrap + /// around so that the returned result always lies within 0 -> 2Ï€ radians (0 -> 360 degrees). + fn wrapping_radians_add(self, other: Self::Type) -> Self::Type; - /// Subtracts two angle values in radians returning the result. The subtraction will wrap - /// around so that the returned result always lies within 0 -> 2Ï€ radians (0 -> 360 degrees). - fn wrapping_radians_sub(self, other: Self::Type) -> Self::Type; + /// Subtracts two angle values in radians returning the result. The subtraction will wrap + /// around so that the returned result always lies within 0 -> 2Ï€ radians (0 -> 360 degrees). + fn wrapping_radians_sub(self, other: Self::Type) -> Self::Type; } impl WrappingRadians for f32 { - type Type = f32; + type Type = f32; - fn wrapping_radians_add(self, other: Self::Type) -> Self::Type { - let result = self + other; - if result < RADIANS_0 { - result + RADIANS_360 - } else if result >= RADIANS_360 { - result - RADIANS_360 - } else { - result - } - } + fn wrapping_radians_add(self, other: Self::Type) -> Self::Type { + let result = self + other; + if result < RADIANS_0 { + result + RADIANS_360 + } else if result >= RADIANS_360 { + result - RADIANS_360 + } else { + result + } + } - fn wrapping_radians_sub(self, other: Self::Type) -> Self::Type { - let result = self - other; - if result < RADIANS_0 { - result + RADIANS_360 - } else if result >= RADIANS_360 { - result - RADIANS_360 - } else { - result - } - } + fn wrapping_radians_sub(self, other: Self::Type) -> Self::Type { + let result = self - other; + if result < RADIANS_0 { + result + RADIANS_360 + } else if result >= RADIANS_360 { + result - RADIANS_360 + } else { + result + } + } } #[cfg(test)] pub mod tests { - use super::*; + use super::*; - #[test] - pub fn test_nearly_equal() { - assert!(nearly_equal(4.0, 4.0, 0.1)); - assert!(4.0f32.nearly_equal(4.0, 0.1)); - assert!(nearly_equal(0.1 + 0.2, 0.3, 0.01)); - assert!(!nearly_equal(1.0001, 1.0005, 0.0001)); - } + #[test] + pub fn test_nearly_equal() { + assert!(nearly_equal(4.0, 4.0, 0.1)); + assert!(4.0f32.nearly_equal(4.0, 0.1)); + assert!(nearly_equal(0.1 + 0.2, 0.3, 0.01)); + assert!(!nearly_equal(1.0001, 1.0005, 0.0001)); + } - #[test] - pub fn test_lerp() { - assert!(nearly_equal(15.0, lerp(10.0, 20.0, 0.5), 0.0001)); - assert!(nearly_equal(10.0, lerp(10.0, 20.0, 0.0), 0.0001)); - assert!(nearly_equal(20.0, lerp(10.0, 20.0, 1.0), 0.0001)); - } + #[test] + pub fn test_lerp() { + assert!(nearly_equal(15.0, lerp(10.0, 20.0, 0.5), 0.0001)); + assert!(nearly_equal(10.0, lerp(10.0, 20.0, 0.0), 0.0001)); + assert!(nearly_equal(20.0, lerp(10.0, 20.0, 1.0), 0.0001)); + } - #[test] - pub fn test_inverse_lerp() { - assert_eq!(0.5, inverse_lerp(10.0, 20.0, 15.0f32)) - } + #[test] + pub fn test_inverse_lerp() { + assert_eq!(0.5, inverse_lerp(10.0, 20.0, 15.0f32)) + } - #[test] - pub fn test_angle_between() { - let angle = angle_between(20.0, 20.0, 10.0, 10.0); - assert!(nearly_equal(-RADIANS_135, angle, 0.0001)); - let angle = angle_between(0.0, 0.0, 10.0, 10.0); - assert!(nearly_equal(RADIANS_45, angle, 0.0001)); - let angle = angle_between(5.0, 5.0, 5.0, 5.0); - assert!(nearly_equal(0.0, angle, 0.0001)); - } + #[test] + pub fn test_angle_between() { + let angle = angle_between(20.0, 20.0, 10.0, 10.0); + assert!(nearly_equal(-RADIANS_135, angle, 0.0001)); + let angle = angle_between(0.0, 0.0, 10.0, 10.0); + assert!(nearly_equal(RADIANS_45, angle, 0.0001)); + let angle = angle_between(5.0, 5.0, 5.0, 5.0); + assert!(nearly_equal(0.0, angle, 0.0001)); + } - #[test] - pub fn test_angle_to_direction() { - let (x, y) = angle_to_direction(RADIANS_0); - assert!(nearly_equal(x, 1.0, 0.000001)); - assert!(nearly_equal(y, 0.0, 0.000001)); - let (x, y) = angle_to_direction(RADIANS_45); - assert!(nearly_equal(x, 0.707106, 0.000001)); - assert!(nearly_equal(y, 0.707106, 0.000001)); - let (x, y) = angle_to_direction(RADIANS_225); - assert!(nearly_equal(x, -0.707106, 0.000001)); - assert!(nearly_equal(y, -0.707106, 0.000001)); + #[test] + pub fn test_angle_to_direction() { + let (x, y) = angle_to_direction(RADIANS_0); + assert!(nearly_equal(x, 1.0, 0.000001)); + assert!(nearly_equal(y, 0.0, 0.000001)); + let (x, y) = angle_to_direction(RADIANS_45); + assert!(nearly_equal(x, 0.707106, 0.000001)); + assert!(nearly_equal(y, 0.707106, 0.000001)); + let (x, y) = angle_to_direction(RADIANS_225); + assert!(nearly_equal(x, -0.707106, 0.000001)); + assert!(nearly_equal(y, -0.707106, 0.000001)); - let (x, y) = angle_to_direction(UP); - assert!(nearly_equal(x, 0.0, 0.000001)); - assert!(nearly_equal(y, -1.0, 0.000001)); - let (x, y) = angle_to_direction(DOWN); - assert!(nearly_equal(x, 0.0, 0.000001)); - assert!(nearly_equal(y, 1.0, 0.000001)); - let (x, y) = angle_to_direction(LEFT); - assert!(nearly_equal(x, -1.0, 0.000001)); - assert!(nearly_equal(y, 0.0, 0.000001)); - let (x, y) = angle_to_direction(RIGHT); - assert!(nearly_equal(x, 1.0, 0.000001)); - assert!(nearly_equal(y, 0.0, 0.000001)); - } + let (x, y) = angle_to_direction(UP); + assert!(nearly_equal(x, 0.0, 0.000001)); + assert!(nearly_equal(y, -1.0, 0.000001)); + let (x, y) = angle_to_direction(DOWN); + assert!(nearly_equal(x, 0.0, 0.000001)); + assert!(nearly_equal(y, 1.0, 0.000001)); + let (x, y) = angle_to_direction(LEFT); + assert!(nearly_equal(x, -1.0, 0.000001)); + assert!(nearly_equal(y, 0.0, 0.000001)); + let (x, y) = angle_to_direction(RIGHT); + assert!(nearly_equal(x, 1.0, 0.000001)); + assert!(nearly_equal(y, 0.0, 0.000001)); + } - #[test] - pub fn test_distance_between() { - let x1 = -2.0; - let y1 = 1.0; - let x2 = 4.0; - let y2 = 3.0; - let distance_squared = distance_squared_between(x1, y1, x2, y2); - let distance = distance_between(x1, y1, x2, y2); - assert!(nearly_equal(distance_squared, 40.0000, 0.0001)); - assert!(nearly_equal(distance, 6.3245, 0.0001)); - } + #[test] + pub fn test_distance_between() { + let x1 = -2.0; + let y1 = 1.0; + let x2 = 4.0; + let y2 = 3.0; + let distance_squared = distance_squared_between(x1, y1, x2, y2); + let distance = distance_between(x1, y1, x2, y2); + assert!(nearly_equal(distance_squared, 40.0000, 0.0001)); + assert!(nearly_equal(distance, 6.3245, 0.0001)); + } - #[test] - pub fn test_wrapping_radians() { - assert!(nearly_equal(RADIANS_90, RADIANS_45.wrapping_radians_add(RADIANS_45), 0.0001)); - assert!(nearly_equal(RADIANS_90, RADIANS_180.wrapping_radians_sub(RADIANS_90), 0.0001)); + #[test] + pub fn test_wrapping_radians() { + assert!(nearly_equal(RADIANS_90, RADIANS_45.wrapping_radians_add(RADIANS_45), 0.0001)); + assert!(nearly_equal(RADIANS_90, RADIANS_180.wrapping_radians_sub(RADIANS_90), 0.0001)); - assert!(nearly_equal(RADIANS_45, RADIANS_315.wrapping_radians_add(RADIANS_90), 0.0001)); - assert!(nearly_equal(RADIANS_315, RADIANS_90.wrapping_radians_sub(RADIANS_135), 0.0001)); - } + assert!(nearly_equal(RADIANS_45, RADIANS_315.wrapping_radians_add(RADIANS_90), 0.0001)); + assert!(nearly_equal(RADIANS_315, RADIANS_90.wrapping_radians_sub(RADIANS_135), 0.0001)); + } } diff --git a/libretrogd/src/math/rect.rs b/libretrogd/src/math/rect.rs index 1180007..5e8a237 100644 --- a/libretrogd/src/math/rect.rs +++ b/libretrogd/src/math/rect.rs @@ -1,284 +1,284 @@ /// Represents a 2D rectangle, using integer coordinates and dimensions. #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub struct Rect { - pub x: i32, - pub y: i32, - pub width: u32, - pub height: u32, + pub x: i32, + pub y: i32, + pub width: u32, + pub height: u32, } impl Rect { - #[inline] - pub fn new(x: i32, y: i32, width: u32, height: u32) -> Rect { - Rect { - x, - y, - width, - height, - } - } + #[inline] + pub fn new(x: i32, y: i32, width: u32, height: u32) -> Rect { + Rect { + x, + y, + width, + height, + } + } - /// Creates a new rect from the specified coordinates. Automatically determines if the - /// coordinates provided are swapped (where the right/bottom coordinate is provided before the - /// left/top). All of the coordinates are inclusive. - /// - /// # Arguments - /// - /// * `left`: the left x coordinate - /// * `top`: the top y coordinate - /// * `right`: the right x coordinate - /// * `bottom`: the bottom y coordinate - pub fn from_coords(left: i32, top: i32, right: i32, bottom: i32) -> Rect { - let x; - let y; - let width; - let height; + /// Creates a new rect from the specified coordinates. Automatically determines if the + /// coordinates provided are swapped (where the right/bottom coordinate is provided before the + /// left/top). All of the coordinates are inclusive. + /// + /// # Arguments + /// + /// * `left`: the left x coordinate + /// * `top`: the top y coordinate + /// * `right`: the right x coordinate + /// * `bottom`: the bottom y coordinate + pub fn from_coords(left: i32, top: i32, right: i32, bottom: i32) -> Rect { + let x; + let y; + let width; + let height; - if left <= right { - x = left; - width = (right - left).abs() + 1; - } else { - x = right; - width = (left - right).abs() + 1; - } + if left <= right { + x = left; + width = (right - left).abs() + 1; + } else { + x = right; + width = (left - right).abs() + 1; + } - if top <= bottom { - y = top; - height = (bottom - top).abs() + 1; - } else { - y = bottom; - height = (top - bottom).abs() + 1; - } + if top <= bottom { + y = top; + height = (bottom - top).abs() + 1; + } else { + y = bottom; + height = (top - bottom).abs() + 1; + } - Rect { - x, - y, - width: width as u32, - height: height as u32, - } - } + Rect { + x, + y, + width: width as u32, + height: height as u32, + } + } - pub fn set_from_coords(&mut self, left: i32, top: i32, right: i32, bottom: i32) { - if left <= right { - self.x = left; - self.width = ((right - left).abs() + 1) as u32; - } else { - self.x = right; - self.width = ((left - right).abs() + 1) as u32; - } + pub fn set_from_coords(&mut self, left: i32, top: i32, right: i32, bottom: i32) { + if left <= right { + self.x = left; + self.width = ((right - left).abs() + 1) as u32; + } else { + self.x = right; + self.width = ((left - right).abs() + 1) as u32; + } - if top <= bottom { - self.y = top; - self.height = ((bottom - top).abs() + 1) as u32; - } else { - self.y = bottom; - self.height = ((top - bottom).abs() + 1) as u32; - } - } + if top <= bottom { + self.y = top; + self.height = ((bottom - top).abs() + 1) as u32; + } else { + self.y = bottom; + self.height = ((top - bottom).abs() + 1) as u32; + } + } - /// Calculates the right-most x coordinate contained by this rect. - #[inline] - pub fn right(&self) -> i32 { - if self.width > 0 { - self.x + self.width as i32 - 1 - } else { - self.x - } - } + /// Calculates the right-most x coordinate contained by this rect. + #[inline] + pub fn right(&self) -> i32 { + if self.width > 0 { + self.x + self.width as i32 - 1 + } else { + self.x + } + } - /// Calculates the bottom-most y coordinate contained by this rect. - #[inline] - pub fn bottom(&self) -> i32 { - if self.height > 0 { - self.y + self.height as i32 - 1 - } else { - self.y - } - } + /// Calculates the bottom-most y coordinate contained by this rect. + #[inline] + pub fn bottom(&self) -> i32 { + if self.height > 0 { + self.y + self.height as i32 - 1 + } else { + self.y + } + } - /// Returns true if the given point is contained within the bounds of this rect. - pub fn contains_point(&self, x: i32, y: i32) -> bool { - (self.x <= x) && (self.right() >= x) && (self.y <= y) && (self.bottom() >= y) - } + /// Returns true if the given point is contained within the bounds of this rect. + pub fn contains_point(&self, x: i32, y: i32) -> bool { + (self.x <= x) && (self.right() >= x) && (self.y <= y) && (self.bottom() >= y) + } - /// Returns true if the given rect is contained completely within the bounds of this rect. - pub fn contains_rect(&self, other: &Rect) -> bool { - (other.x >= self.x && other.x < self.right()) - && (other.right() > self.x && other.right() <= self.right()) - && (other.y >= self.y && other.y < self.bottom()) - && (other.bottom() > self.y && other.bottom() <= self.bottom()) - } + /// Returns true if the given rect is contained completely within the bounds of this rect. + pub fn contains_rect(&self, other: &Rect) -> bool { + (other.x >= self.x && other.x < self.right()) + && (other.right() > self.x && other.right() <= self.right()) + && (other.y >= self.y && other.y < self.bottom()) + && (other.bottom() > self.y && other.bottom() <= self.bottom()) + } - /// Returns true if the given rect at least partially overlaps the bounds of this rect. - pub fn overlaps(&self, other: &Rect) -> bool { - (self.x <= other.right()) - && (self.right() >= other.x) - && (self.y <= other.bottom()) - && (self.bottom() >= other.y) - } + /// Returns true if the given rect at least partially overlaps the bounds of this rect. + pub fn overlaps(&self, other: &Rect) -> bool { + (self.x <= other.right()) + && (self.right() >= other.x) + && (self.y <= other.bottom()) + && (self.bottom() >= other.y) + } - pub fn clamp_to(&mut self, other: &Rect) -> bool { - if !self.overlaps(other) { - // not possible to clamp this rect to the other rect as they do not overlap at all - false - } else { - // the rects at least partially overlap, so we will clamp this rect to the overlapping - // region of the other rect - let mut x1 = self.x; - let mut y1 = self.y; - let mut x2 = self.right(); - let mut y2 = self.bottom(); - if y1 < other.y { - y1 = other.y; - } - if y1 > other.bottom() { - y1 = other.bottom(); - } - if y2 < other.y { - y2 = other.y; - } - if y2 > other.bottom() { - y2 = other.bottom(); - } - if x1 < other.x { - x1 = other.x; - } - if x1 > other.right() { - x1 = other.right(); - } - if x2 < other.x { - x2 = other.x; - } - if x2 > other.right() { - x2 = other.right(); - } + pub fn clamp_to(&mut self, other: &Rect) -> bool { + if !self.overlaps(other) { + // not possible to clamp this rect to the other rect as they do not overlap at all + false + } else { + // the rects at least partially overlap, so we will clamp this rect to the overlapping + // region of the other rect + let mut x1 = self.x; + let mut y1 = self.y; + let mut x2 = self.right(); + let mut y2 = self.bottom(); + if y1 < other.y { + y1 = other.y; + } + if y1 > other.bottom() { + y1 = other.bottom(); + } + if y2 < other.y { + y2 = other.y; + } + if y2 > other.bottom() { + y2 = other.bottom(); + } + if x1 < other.x { + x1 = other.x; + } + if x1 > other.right() { + x1 = other.right(); + } + if x2 < other.x { + x2 = other.x; + } + if x2 > other.right() { + x2 = other.right(); + } - self.set_from_coords(x1, y1, x2, y2); - true - } - } + self.set_from_coords(x1, y1, x2, y2); + true + } + } } #[cfg(test)] pub mod tests { - use super::*; + use super::*; - #[test] - pub fn right_and_left() { - let rect = Rect { - x: 5, - y: 6, - width: 16, - height: 12, - }; - assert_eq!(20, rect.right()); - assert_eq!(17, rect.bottom()); + #[test] + pub fn right_and_left() { + let rect = Rect { + x: 5, + y: 6, + width: 16, + height: 12, + }; + assert_eq!(20, rect.right()); + assert_eq!(17, rect.bottom()); - let rect = Rect { - x: -11, - y: -25, - width: 16, - height: 12, - }; - assert_eq!(4, rect.right()); - assert_eq!(-14, rect.bottom()); - } + let rect = Rect { + x: -11, + y: -25, + width: 16, + height: 12, + }; + assert_eq!(4, rect.right()); + assert_eq!(-14, rect.bottom()); + } - #[test] - pub fn create_from_coords() { - let rect = Rect::from_coords(10, 15, 20, 30); - assert_eq!(10, rect.x); - assert_eq!(15, rect.y); - assert_eq!(11, rect.width); - assert_eq!(16, rect.height); - assert_eq!(20, rect.right()); - assert_eq!(30, rect.bottom()); + #[test] + pub fn create_from_coords() { + let rect = Rect::from_coords(10, 15, 20, 30); + assert_eq!(10, rect.x); + assert_eq!(15, rect.y); + assert_eq!(11, rect.width); + assert_eq!(16, rect.height); + assert_eq!(20, rect.right()); + assert_eq!(30, rect.bottom()); - let rect = Rect::from_coords(-5, -13, 6, -2); - assert_eq!(-5, rect.x); - assert_eq!(-13, rect.y); - assert_eq!(12, rect.width); - assert_eq!(12, rect.height); - assert_eq!(6, rect.right()); - assert_eq!(-2, rect.bottom()); - } + let rect = Rect::from_coords(-5, -13, 6, -2); + assert_eq!(-5, rect.x); + assert_eq!(-13, rect.y); + assert_eq!(12, rect.width); + assert_eq!(12, rect.height); + assert_eq!(6, rect.right()); + assert_eq!(-2, rect.bottom()); + } - #[test] - pub fn create_from_coords_with_swapped_order() { - let rect = Rect::from_coords(20, 30, 10, 15); - assert_eq!(10, rect.x); - assert_eq!(15, rect.y); - assert_eq!(11, rect.width); - assert_eq!(16, rect.height); - assert_eq!(20, rect.right()); - assert_eq!(30, rect.bottom()); + #[test] + pub fn create_from_coords_with_swapped_order() { + let rect = Rect::from_coords(20, 30, 10, 15); + assert_eq!(10, rect.x); + assert_eq!(15, rect.y); + assert_eq!(11, rect.width); + assert_eq!(16, rect.height); + assert_eq!(20, rect.right()); + assert_eq!(30, rect.bottom()); - let rect = Rect::from_coords(6, -2, -5, -13); - assert_eq!(-5, rect.x); - assert_eq!(-13, rect.y); - assert_eq!(12, rect.width); - assert_eq!(12, rect.height); - assert_eq!(6, rect.right()); - assert_eq!(-2, rect.bottom()); - } + let rect = Rect::from_coords(6, -2, -5, -13); + assert_eq!(-5, rect.x); + assert_eq!(-13, rect.y); + assert_eq!(12, rect.width); + assert_eq!(12, rect.height); + assert_eq!(6, rect.right()); + assert_eq!(-2, rect.bottom()); + } - #[test] - pub fn test_contains_point() { - let r = Rect::from_coords(10, 10, 20, 20); + #[test] + pub fn test_contains_point() { + let r = Rect::from_coords(10, 10, 20, 20); - assert!(r.contains_point(10, 10)); - assert!(r.contains_point(15, 15)); - assert!(r.contains_point(20, 20)); + assert!(r.contains_point(10, 10)); + assert!(r.contains_point(15, 15)); + assert!(r.contains_point(20, 20)); - assert!(!r.contains_point(12, 30)); - assert!(!r.contains_point(8, 12)); - assert!(!r.contains_point(25, 16)); - assert!(!r.contains_point(17, 4)); - } + assert!(!r.contains_point(12, 30)); + assert!(!r.contains_point(8, 12)); + assert!(!r.contains_point(25, 16)); + assert!(!r.contains_point(17, 4)); + } - #[test] - pub fn test_contains_rect() { - let r = Rect::from_coords(10, 10, 20, 20); + #[test] + pub fn test_contains_rect() { + let r = Rect::from_coords(10, 10, 20, 20); - assert!(r.contains_rect(&Rect::from_coords(12, 12, 15, 15))); - assert!(r.contains_rect(&Rect::from_coords(10, 10, 15, 15))); - assert!(r.contains_rect(&Rect::from_coords(15, 15, 20, 20))); - assert!(r.contains_rect(&Rect::from_coords(10, 12, 20, 15))); - assert!(r.contains_rect(&Rect::from_coords(12, 10, 15, 20))); + assert!(r.contains_rect(&Rect::from_coords(12, 12, 15, 15))); + assert!(r.contains_rect(&Rect::from_coords(10, 10, 15, 15))); + assert!(r.contains_rect(&Rect::from_coords(15, 15, 20, 20))); + assert!(r.contains_rect(&Rect::from_coords(10, 12, 20, 15))); + assert!(r.contains_rect(&Rect::from_coords(12, 10, 15, 20))); - assert!(!r.contains_rect(&Rect::from_coords(5, 5, 15, 15))); - assert!(!r.contains_rect(&Rect::from_coords(15, 15, 25, 25))); + assert!(!r.contains_rect(&Rect::from_coords(5, 5, 15, 15))); + assert!(!r.contains_rect(&Rect::from_coords(15, 15, 25, 25))); - assert!(!r.contains_rect(&Rect::from_coords(2, 2, 8, 4))); - assert!(!r.contains_rect(&Rect::from_coords(12, 21, 18, 25))); - assert!(!r.contains_rect(&Rect::from_coords(22, 12, 32, 17))); - } + assert!(!r.contains_rect(&Rect::from_coords(2, 2, 8, 4))); + assert!(!r.contains_rect(&Rect::from_coords(12, 21, 18, 25))); + assert!(!r.contains_rect(&Rect::from_coords(22, 12, 32, 17))); + } - #[test] - pub fn test_overlaps() { - let r = Rect::from_coords(10, 10, 20, 20); + #[test] + pub fn test_overlaps() { + let r = Rect::from_coords(10, 10, 20, 20); - assert!(r.overlaps(&Rect::from_coords(12, 12, 15, 15))); - assert!(r.overlaps(&Rect::from_coords(10, 10, 15, 15))); - assert!(r.overlaps(&Rect::from_coords(15, 15, 20, 20))); - assert!(r.overlaps(&Rect::from_coords(10, 12, 20, 15))); - assert!(r.overlaps(&Rect::from_coords(12, 10, 15, 20))); + assert!(r.overlaps(&Rect::from_coords(12, 12, 15, 15))); + assert!(r.overlaps(&Rect::from_coords(10, 10, 15, 15))); + assert!(r.overlaps(&Rect::from_coords(15, 15, 20, 20))); + assert!(r.overlaps(&Rect::from_coords(10, 12, 20, 15))); + assert!(r.overlaps(&Rect::from_coords(12, 10, 15, 20))); - assert!(r.overlaps(&Rect::from_coords(12, 5, 18, 10))); - assert!(r.overlaps(&Rect::from_coords(13, 20, 16, 25))); - assert!(r.overlaps(&Rect::from_coords(5, 12, 10, 18))); - assert!(r.overlaps(&Rect::from_coords(20, 13, 25, 16))); + assert!(r.overlaps(&Rect::from_coords(12, 5, 18, 10))); + assert!(r.overlaps(&Rect::from_coords(13, 20, 16, 25))); + assert!(r.overlaps(&Rect::from_coords(5, 12, 10, 18))); + assert!(r.overlaps(&Rect::from_coords(20, 13, 25, 16))); - assert!(r.overlaps(&Rect::from_coords(5, 5, 15, 15))); - assert!(r.overlaps(&Rect::from_coords(15, 15, 25, 25))); + assert!(r.overlaps(&Rect::from_coords(5, 5, 15, 15))); + assert!(r.overlaps(&Rect::from_coords(15, 15, 25, 25))); - assert!(!r.overlaps(&Rect::from_coords(2, 2, 8, 4))); - assert!(!r.overlaps(&Rect::from_coords(12, 21, 18, 25))); - assert!(!r.overlaps(&Rect::from_coords(22, 12, 32, 17))); + assert!(!r.overlaps(&Rect::from_coords(2, 2, 8, 4))); + assert!(!r.overlaps(&Rect::from_coords(12, 21, 18, 25))); + assert!(!r.overlaps(&Rect::from_coords(22, 12, 32, 17))); - assert!(!r.overlaps(&Rect::from_coords(12, 5, 18, 9))); - assert!(!r.overlaps(&Rect::from_coords(13, 21, 16, 25))); - assert!(!r.overlaps(&Rect::from_coords(5, 12, 9, 18))); - assert!(!r.overlaps(&Rect::from_coords(21, 13, 25, 16))); - } + assert!(!r.overlaps(&Rect::from_coords(12, 5, 18, 9))); + assert!(!r.overlaps(&Rect::from_coords(13, 21, 16, 25))); + assert!(!r.overlaps(&Rect::from_coords(5, 12, 9, 18))); + assert!(!r.overlaps(&Rect::from_coords(21, 13, 25, 16))); + } } diff --git a/libretrogd/src/math/vector2.rs b/libretrogd/src/math/vector2.rs index 57ea899..dbeddfe 100644 --- a/libretrogd/src/math/vector2.rs +++ b/libretrogd/src/math/vector2.rs @@ -5,478 +5,478 @@ use crate::math::*; /// Represents a 2D vector and provides common methods for vector math. #[derive(Debug, Copy, Clone, PartialEq)] pub struct Vector2 { - pub x: f32, - pub y: f32, + pub x: f32, + pub y: f32, } impl Vector2 { - pub const ZERO: Vector2 = Vector2 { x: 0.0, y: 0.0 }; + pub const ZERO: Vector2 = Vector2 { x: 0.0, y: 0.0 }; - pub const UP: Vector2 = Vector2 { x: 0.0, y: -1.0 }; - pub const DOWN: Vector2 = Vector2 { x: 0.0, y: 1.0 }; - pub const LEFT: Vector2 = Vector2 { x: -1.0, y: 0.0 }; - pub const RIGHT: Vector2 = Vector2 { x: 1.0, y: 0.0 }; + pub const UP: Vector2 = Vector2 { x: 0.0, y: -1.0 }; + pub const DOWN: Vector2 = Vector2 { x: 0.0, y: 1.0 }; + pub const LEFT: Vector2 = Vector2 { x: -1.0, y: 0.0 }; + pub const RIGHT: Vector2 = Vector2 { x: 1.0, y: 0.0 }; - pub const X_AXIS: Vector2 = Vector2 { x: 1.0, y: 0.0 }; - pub const Y_AXIS: Vector2 = Vector2 { x: 0.0, y: 1.0 }; + pub const X_AXIS: Vector2 = Vector2 { x: 1.0, y: 0.0 }; + pub const Y_AXIS: Vector2 = Vector2 { x: 0.0, y: 1.0 }; - /// Creates a vector with the specified X and Y components. - #[inline] - pub fn new(x: f32, y: f32) -> Vector2 { - Vector2 { x, y } - } + /// Creates a vector with the specified X and Y components. + #[inline] + pub fn new(x: f32, y: f32) -> Vector2 { + Vector2 { x, y } + } - /// Creates a normalized vector that points in the same direction as the given angle. - #[inline] - pub fn from_angle(radians: f32) -> Vector2 { - let (x, y) = angle_to_direction(radians); - Vector2 { x, y } - } + /// Creates a normalized vector that points in the same direction as the given angle. + #[inline] + pub fn from_angle(radians: f32) -> Vector2 { + let (x, y) = angle_to_direction(radians); + Vector2 { x, y } + } - /// Calculates the distance between this and another vector. - #[inline] - pub fn distance(&self, other: &Vector2) -> f32 { - self.distance_squared(other).sqrt() - } + /// Calculates the distance between this and another vector. + #[inline] + pub fn distance(&self, other: &Vector2) -> f32 { + self.distance_squared(other).sqrt() + } - /// Calculates the squared distance between this and another vector. - #[inline] - pub fn distance_squared(&self, other: &Vector2) -> f32 { - (other.x - self.x) * (other.x - self.x) + (other.y - self.y) * (other.y - self.y) - } + /// Calculates the squared distance between this and another vector. + #[inline] + pub fn distance_squared(&self, other: &Vector2) -> f32 { + (other.x - self.x) * (other.x - self.x) + (other.y - self.y) * (other.y - self.y) + } - /// Calculates the dot product of this and another vector. - #[inline] - pub fn dot(&self, other: &Vector2) -> f32 { - (self.x * other.x) + (self.y * other.y) - } + /// Calculates the dot product of this and another vector. + #[inline] + pub fn dot(&self, other: &Vector2) -> f32 { + (self.x * other.x) + (self.y * other.y) + } - /// Calculates the length (a.k.a. magnitude) of this vector. - #[inline] - pub fn length(&self) -> f32 { - self.length_squared().sqrt() - } + /// Calculates the length (a.k.a. magnitude) of this vector. + #[inline] + pub fn length(&self) -> f32 { + self.length_squared().sqrt() + } - /// Calculates the squared length of this vector. - #[inline] - pub fn length_squared(&self) -> f32 { - (self.x * self.x) + (self.y * self.y) - } + /// Calculates the squared length of this vector. + #[inline] + pub fn length_squared(&self) -> f32 { + (self.x * self.x) + (self.y * self.y) + } - /// Returns a normalized vector from this vector. - pub fn normalize(&self) -> Vector2 { - let inverse_length = 1.0 / self.length(); - Vector2 { - x: self.x * inverse_length, - y: self.y * inverse_length, - } - } + /// Returns a normalized vector from this vector. + pub fn normalize(&self) -> Vector2 { + let inverse_length = 1.0 / self.length(); + Vector2 { + x: self.x * inverse_length, + y: self.y * inverse_length, + } + } - /// Returns an extended (or shrunk) vector from this vector, where the returned vector will - /// have a length exactly matching the specified length, but will retain the same direction. - pub fn extend(&self, length: f32) -> Vector2 { - *self * (length / self.length()) - } + /// Returns an extended (or shrunk) vector from this vector, where the returned vector will + /// have a length exactly matching the specified length, but will retain the same direction. + pub fn extend(&self, length: f32) -> Vector2 { + *self * (length / self.length()) + } - /// Returns the angle (in radians) equivalent to the direction of this vector. - #[inline] - pub fn angle(&self) -> f32 { - self.y.atan2(self.x) - } + /// Returns the angle (in radians) equivalent to the direction of this vector. + #[inline] + pub fn angle(&self) -> f32 { + self.y.atan2(self.x) + } - /// Calculates the angle (in radians) between the this and another vector. - #[inline] - pub fn angle_between(&self, other: &Vector2) -> f32 { - angle_between(self.x, self.y, other.x, other.y) - } + /// Calculates the angle (in radians) between the this and another vector. + #[inline] + pub fn angle_between(&self, other: &Vector2) -> f32 { + angle_between(self.x, self.y, other.x, other.y) + } - /// Returns true if this vector is nearly equal to the zero vector (0.0, 0.0). - #[inline] - pub fn almost_zero(&self, epsilon: f32) -> bool { - self.nearly_equal(Vector2::ZERO, epsilon) - } + /// Returns true if this vector is nearly equal to the zero vector (0.0, 0.0). + #[inline] + pub fn almost_zero(&self, epsilon: f32) -> bool { + self.nearly_equal(Vector2::ZERO, epsilon) + } } impl Neg for Vector2 { - type Output = Self; + type Output = Self; - #[inline] - fn neg(self) -> Self::Output { - Vector2 { - x: -self.x, - y: -self.y, - } - } + #[inline] + fn neg(self) -> Self::Output { + Vector2 { + x: -self.x, + y: -self.y, + } + } } impl Add for Vector2 { - type Output = Self; + type Output = Self; - #[inline] - fn add(self, rhs: Self) -> Self::Output { - Vector2 { - x: self.x + rhs.x, - y: self.y + rhs.y, - } - } + #[inline] + fn add(self, rhs: Self) -> Self::Output { + Vector2 { + x: self.x + rhs.x, + y: self.y + rhs.y, + } + } } impl AddAssign for Vector2 { - #[inline] - fn add_assign(&mut self, rhs: Self) { - self.x += rhs.x; - self.y += rhs.y; - } + #[inline] + fn add_assign(&mut self, rhs: Self) { + self.x += rhs.x; + self.y += rhs.y; + } } impl Sub for Vector2 { - type Output = Self; + type Output = Self; - #[inline] - fn sub(self, rhs: Self) -> Self::Output { - Vector2 { - x: self.x - rhs.x, - y: self.y - rhs.y, - } - } + #[inline] + fn sub(self, rhs: Self) -> Self::Output { + Vector2 { + x: self.x - rhs.x, + y: self.y - rhs.y, + } + } } impl SubAssign for Vector2 { - #[inline] - fn sub_assign(&mut self, rhs: Self) { - self.x -= rhs.x; - self.y -= rhs.y; - } + #[inline] + fn sub_assign(&mut self, rhs: Self) { + self.x -= rhs.x; + self.y -= rhs.y; + } } impl Mul for Vector2 { - type Output = Self; + type Output = Self; - #[inline] - fn mul(self, rhs: Self) -> Self::Output { - Vector2 { - x: self.x * rhs.x, - y: self.y * rhs.y, - } - } + #[inline] + fn mul(self, rhs: Self) -> Self::Output { + Vector2 { + x: self.x * rhs.x, + y: self.y * rhs.y, + } + } } impl MulAssign for Vector2 { - #[inline] - fn mul_assign(&mut self, rhs: Self) { - self.x *= rhs.x; - self.y *= rhs.y; - } + #[inline] + fn mul_assign(&mut self, rhs: Self) { + self.x *= rhs.x; + self.y *= rhs.y; + } } impl Div for Vector2 { - type Output = Self; + type Output = Self; - #[inline] - fn div(self, rhs: Self) -> Self::Output { - Vector2 { - x: self.x / rhs.x, - y: self.y / rhs.y, - } - } + #[inline] + fn div(self, rhs: Self) -> Self::Output { + Vector2 { + x: self.x / rhs.x, + y: self.y / rhs.y, + } + } } impl DivAssign for Vector2 { - #[inline] - fn div_assign(&mut self, rhs: Self) { - self.x /= rhs.x; - self.y /= rhs.y; - } + #[inline] + fn div_assign(&mut self, rhs: Self) { + self.x /= rhs.x; + self.y /= rhs.y; + } } impl Mul for Vector2 { - type Output = Self; + type Output = Self; - #[inline] - fn mul(self, rhs: f32) -> Self::Output { - Vector2 { - x: self.x * rhs, - y: self.y * rhs, - } - } + #[inline] + fn mul(self, rhs: f32) -> Self::Output { + Vector2 { + x: self.x * rhs, + y: self.y * rhs, + } + } } impl MulAssign for Vector2 { - #[inline] - fn mul_assign(&mut self, rhs: f32) { - self.x *= rhs; - self.y *= rhs; - } + #[inline] + fn mul_assign(&mut self, rhs: f32) { + self.x *= rhs; + self.y *= rhs; + } } impl Div for Vector2 { - type Output = Self; + type Output = Self; - #[inline] - fn div(self, rhs: f32) -> Self::Output { - Vector2 { - x: self.x / rhs, - y: self.y / rhs, - } - } + #[inline] + fn div(self, rhs: f32) -> Self::Output { + Vector2 { + x: self.x / rhs, + y: self.y / rhs, + } + } } impl DivAssign for Vector2 { - #[inline] - fn div_assign(&mut self, rhs: f32) { - self.x /= rhs; - self.y /= rhs; - } + #[inline] + fn div_assign(&mut self, rhs: f32) { + self.x /= rhs; + self.y /= rhs; + } } impl NearlyEqual for Vector2 { - type Output = Self; + type Output = Self; - #[inline(always)] - fn nearly_equal(self, other: Self::Output, epsilon: f32) -> bool { - nearly_equal(self.x, other.x, epsilon) && nearly_equal(self.y, other.y, epsilon) - } + #[inline(always)] + fn nearly_equal(self, other: Self::Output, epsilon: f32) -> bool { + nearly_equal(self.x, other.x, epsilon) && nearly_equal(self.y, other.y, epsilon) + } } impl MulAssign for Vector2 { - #[rustfmt::skip] - #[inline] - fn mul_assign(&mut self, rhs: Matrix3x3) { - let x = self.x * rhs.m[Matrix3x3::M11] + self.y * rhs.m[Matrix3x3::M12] + rhs.m[Matrix3x3::M13] + rhs.m[Matrix3x3::M31]; - let y = self.x * rhs.m[Matrix3x3::M21] + self.y * rhs.m[Matrix3x3::M22] + rhs.m[Matrix3x3::M23] + rhs.m[Matrix3x3::M32]; - self.x = x; - self.y = y; - } + #[rustfmt::skip] + #[inline] + fn mul_assign(&mut self, rhs: Matrix3x3) { + let x = self.x * rhs.m[Matrix3x3::M11] + self.y * rhs.m[Matrix3x3::M12] + rhs.m[Matrix3x3::M13] + rhs.m[Matrix3x3::M31]; + let y = self.x * rhs.m[Matrix3x3::M21] + self.y * rhs.m[Matrix3x3::M22] + rhs.m[Matrix3x3::M23] + rhs.m[Matrix3x3::M32]; + self.x = x; + self.y = y; + } } #[cfg(test)] pub mod tests { - use super::*; + use super::*; - #[test] - pub fn test_new() { - let v = Vector2::new(3.0, 7.0); - assert!(nearly_equal(v.x, 3.0, 0.0001)); - assert!(nearly_equal(v.y, 7.0, 0.0001)); - } + #[test] + pub fn test_new() { + let v = Vector2::new(3.0, 7.0); + assert!(nearly_equal(v.x, 3.0, 0.0001)); + assert!(nearly_equal(v.y, 7.0, 0.0001)); + } - #[test] - pub fn test_neg() { - let v = Vector2 { x: 1.0, y: 2.0 }; - let neg = -v; - assert!(nearly_equal(neg.x, -1.0, 0.0001)); - assert!(nearly_equal(neg.y, -2.0, 0.0001)); - } + #[test] + pub fn test_neg() { + let v = Vector2 { x: 1.0, y: 2.0 }; + let neg = -v; + assert!(nearly_equal(neg.x, -1.0, 0.0001)); + assert!(nearly_equal(neg.y, -2.0, 0.0001)); + } - #[test] - pub fn test_add() { - let a = Vector2 { x: 3.0, y: 4.0 }; - let b = Vector2 { x: 1.0, y: 2.0 }; - let c = a + b; - assert!(nearly_equal(c.x, 4.0, 0.0001)); - assert!(nearly_equal(c.y, 6.0, 0.0001)); + #[test] + pub fn test_add() { + let a = Vector2 { x: 3.0, y: 4.0 }; + let b = Vector2 { x: 1.0, y: 2.0 }; + let c = a + b; + assert!(nearly_equal(c.x, 4.0, 0.0001)); + assert!(nearly_equal(c.y, 6.0, 0.0001)); - let mut a = Vector2 { x: 3.0, y: 4.0 }; - let b = Vector2 { x: 1.0, y: 2.0 }; - a += b; - assert!(nearly_equal(a.x, 4.0, 0.0001)); - assert!(nearly_equal(a.y, 6.0, 0.0001)); - } + let mut a = Vector2 { x: 3.0, y: 4.0 }; + let b = Vector2 { x: 1.0, y: 2.0 }; + a += b; + assert!(nearly_equal(a.x, 4.0, 0.0001)); + assert!(nearly_equal(a.y, 6.0, 0.0001)); + } - #[test] - pub fn test_sub() { - let a = Vector2 { x: 3.0, y: 4.0 }; - let b = Vector2 { x: 1.0, y: 2.0 }; - let c = a - b; - assert!(nearly_equal(c.x, 2.0, 0.0001)); - assert!(nearly_equal(c.y, 2.0, 0.0001)); + #[test] + pub fn test_sub() { + let a = Vector2 { x: 3.0, y: 4.0 }; + let b = Vector2 { x: 1.0, y: 2.0 }; + let c = a - b; + assert!(nearly_equal(c.x, 2.0, 0.0001)); + assert!(nearly_equal(c.y, 2.0, 0.0001)); - let mut a = Vector2 { x: 3.0, y: 4.0 }; - let b = Vector2 { x: 1.0, y: 2.0 }; - a -= b; - assert!(nearly_equal(a.x, 2.0, 0.0001)); - assert!(nearly_equal(a.y, 2.0, 0.0001)); - } + let mut a = Vector2 { x: 3.0, y: 4.0 }; + let b = Vector2 { x: 1.0, y: 2.0 }; + a -= b; + assert!(nearly_equal(a.x, 2.0, 0.0001)); + assert!(nearly_equal(a.y, 2.0, 0.0001)); + } - #[test] - pub fn test_mul() { - let a = Vector2 { x: 2.5, y: 6.0 }; - let b = Vector2 { x: 1.25, y: 2.0 }; - let c = a * b; - assert!(nearly_equal(c.x, 3.125, 0.0001)); - assert!(nearly_equal(c.y, 12.0, 0.0001)); + #[test] + pub fn test_mul() { + let a = Vector2 { x: 2.5, y: 6.0 }; + let b = Vector2 { x: 1.25, y: 2.0 }; + let c = a * b; + assert!(nearly_equal(c.x, 3.125, 0.0001)); + assert!(nearly_equal(c.y, 12.0, 0.0001)); - let mut a = Vector2 { x: 2.5, y: 6.0 }; - let b = Vector2 { x: 1.25, y: 2.0 }; - a *= b; - assert!(nearly_equal(a.x, 3.125, 0.0001)); - assert!(nearly_equal(a.y, 12.0, 0.0001)); - } + let mut a = Vector2 { x: 2.5, y: 6.0 }; + let b = Vector2 { x: 1.25, y: 2.0 }; + a *= b; + assert!(nearly_equal(a.x, 3.125, 0.0001)); + assert!(nearly_equal(a.y, 12.0, 0.0001)); + } - #[test] - pub fn test_div() { - let a = Vector2 { x: 2.5, y: 6.0 }; - let b = Vector2 { x: 1.25, y: 2.0 }; - let c = a / b; - assert!(nearly_equal(c.x, 2.0, 0.0001)); - assert!(nearly_equal(c.y, 3.0, 0.0001)); + #[test] + pub fn test_div() { + let a = Vector2 { x: 2.5, y: 6.0 }; + let b = Vector2 { x: 1.25, y: 2.0 }; + let c = a / b; + assert!(nearly_equal(c.x, 2.0, 0.0001)); + assert!(nearly_equal(c.y, 3.0, 0.0001)); - let mut a = Vector2 { x: 2.5, y: 6.0 }; - let b = Vector2 { x: 1.25, y: 2.0 }; - a /= b; - assert!(nearly_equal(a.x, 2.0, 0.0001)); - assert!(nearly_equal(a.y, 3.0, 0.0001)); - } + let mut a = Vector2 { x: 2.5, y: 6.0 }; + let b = Vector2 { x: 1.25, y: 2.0 }; + a /= b; + assert!(nearly_equal(a.x, 2.0, 0.0001)); + assert!(nearly_equal(a.y, 3.0, 0.0001)); + } - #[test] - pub fn test_scalar_mul() { - let a = Vector2 { x: 1.0, y: 2.0 }; - let b = a * 2.0; - assert!(nearly_equal(b.x, 2.0, 0.0001)); - assert!(nearly_equal(b.y, 4.0, 0.0001)); + #[test] + pub fn test_scalar_mul() { + let a = Vector2 { x: 1.0, y: 2.0 }; + let b = a * 2.0; + assert!(nearly_equal(b.x, 2.0, 0.0001)); + assert!(nearly_equal(b.y, 4.0, 0.0001)); - let mut a = Vector2 { x: 1.0, y: 2.0 }; - a *= 2.0; - assert!(nearly_equal(b.x, 2.0, 0.0001)); - assert!(nearly_equal(b.y, 4.0, 0.0001)); - } + let mut a = Vector2 { x: 1.0, y: 2.0 }; + a *= 2.0; + assert!(nearly_equal(b.x, 2.0, 0.0001)); + assert!(nearly_equal(b.y, 4.0, 0.0001)); + } - #[test] - pub fn test_scalar_div() { - let a = Vector2 { x: 1.0, y: 2.0 }; - let b = a / 2.0; - assert!(nearly_equal(b.x, 0.5, 0.0001)); - assert!(nearly_equal(b.y, 1.0, 0.0001)); + #[test] + pub fn test_scalar_div() { + let a = Vector2 { x: 1.0, y: 2.0 }; + let b = a / 2.0; + assert!(nearly_equal(b.x, 0.5, 0.0001)); + assert!(nearly_equal(b.y, 1.0, 0.0001)); - let mut a = Vector2 { x: 1.0, y: 2.0 }; - a /= 2.0; - assert!(nearly_equal(b.x, 0.5, 0.0001)); - assert!(nearly_equal(b.y, 1.0, 0.0001)); - } + let mut a = Vector2 { x: 1.0, y: 2.0 }; + a /= 2.0; + assert!(nearly_equal(b.x, 0.5, 0.0001)); + assert!(nearly_equal(b.y, 1.0, 0.0001)); + } - #[test] - pub fn test_nearly_equal() { - let a = Vector2 { x: 3.4, y: -7.1 }; - let b = Vector2 { x: 3.5, y: -7.1 }; - assert!(!a.nearly_equal(b, 0.0001)); + #[test] + pub fn test_nearly_equal() { + let a = Vector2 { x: 3.4, y: -7.1 }; + let b = Vector2 { x: 3.5, y: -7.1 }; + assert!(!a.nearly_equal(b, 0.0001)); - let a = Vector2 { x: 2.0, y: 4.0 }; - let b = Vector2 { x: 2.0, y: 4.0 }; - assert!(a.nearly_equal(b, 0.0001)); - } + let a = Vector2 { x: 2.0, y: 4.0 }; + let b = Vector2 { x: 2.0, y: 4.0 }; + assert!(a.nearly_equal(b, 0.0001)); + } - #[test] - pub fn test_length() { - let v = Vector2 { x: 6.0, y: 8.0 }; - let length_squared = v.length_squared(); - let length = v.length(); - assert!(nearly_equal(length_squared, 100.0, 0.0001)); - assert!(nearly_equal(length, 10.0, 0.0001)); - } + #[test] + pub fn test_length() { + let v = Vector2 { x: 6.0, y: 8.0 }; + let length_squared = v.length_squared(); + let length = v.length(); + assert!(nearly_equal(length_squared, 100.0, 0.0001)); + assert!(nearly_equal(length, 10.0, 0.0001)); + } - #[test] - pub fn test_dot() { - let a = Vector2 { x: -6.0, y: 8.0 }; - let b = Vector2 { x: 5.0, y: 12.0 }; - let dot = a.dot(&b); - assert!(nearly_equal(dot, 66.0, 0.0001)); + #[test] + pub fn test_dot() { + let a = Vector2 { x: -6.0, y: 8.0 }; + let b = Vector2 { x: 5.0, y: 12.0 }; + let dot = a.dot(&b); + assert!(nearly_equal(dot, 66.0, 0.0001)); - let a = Vector2 { x: -12.0, y: 16.0 }; - let b = Vector2 { x: 12.0, y: 9.0 }; - let dot = a.dot(&b); - assert!(nearly_equal(dot, 0.0, 0.0001)); - } + let a = Vector2 { x: -12.0, y: 16.0 }; + let b = Vector2 { x: 12.0, y: 9.0 }; + let dot = a.dot(&b); + assert!(nearly_equal(dot, 0.0, 0.0001)); + } - #[test] - pub fn test_distance() { - let a = Vector2 { x: 1.0, y: 1.0 }; - let b = Vector2 { x: 1.0, y: 3.0 }; - let distance_squared = a.distance_squared(&b); - let distance = a.distance(&b); - assert!(nearly_equal(distance_squared, 4.0, 0.0001)); - assert!(nearly_equal(distance, 2.0, 0.0001)); - } + #[test] + pub fn test_distance() { + let a = Vector2 { x: 1.0, y: 1.0 }; + let b = Vector2 { x: 1.0, y: 3.0 }; + let distance_squared = a.distance_squared(&b); + let distance = a.distance(&b); + assert!(nearly_equal(distance_squared, 4.0, 0.0001)); + assert!(nearly_equal(distance, 2.0, 0.0001)); + } - #[test] - pub fn test_normalize() { - let v = Vector2 { x: 3.0, y: 4.0 }; - let normalized = v.normalize(); - assert!(nearly_equal(normalized.x, 0.6, 0.0001)); - assert!(nearly_equal(normalized.y, 0.8, 0.0001)); - } + #[test] + pub fn test_normalize() { + let v = Vector2 { x: 3.0, y: 4.0 }; + let normalized = v.normalize(); + assert!(nearly_equal(normalized.x, 0.6, 0.0001)); + assert!(nearly_equal(normalized.y, 0.8, 0.0001)); + } - #[test] - pub fn test_extend() { - let v = Vector2 { x: 10.0, y: 1.0 }; - let extended = v.extend(2.0); - assert!(nearly_equal(extended.x, 1.990, 0.0001)); - assert!(nearly_equal(extended.y, 0.199, 0.0001)); - } + #[test] + pub fn test_extend() { + let v = Vector2 { x: 10.0, y: 1.0 }; + let extended = v.extend(2.0); + assert!(nearly_equal(extended.x, 1.990, 0.0001)); + assert!(nearly_equal(extended.y, 0.199, 0.0001)); + } - #[test] - #[rustfmt::skip] - pub fn test_angle() { - assert!(nearly_equal(RADIANS_0, Vector2::new(5.0, 0.0).angle(), 0.0001)); - assert!(nearly_equal(RADIANS_45, Vector2::new(5.0, 5.0).angle(), 0.0001)); - assert!(nearly_equal(RADIANS_90, Vector2::new(0.0, 5.0).angle(), 0.0001)); - assert!(nearly_equal(RADIANS_135, Vector2::new(-5.0, 5.0).angle(), 0.0001)); - assert!(nearly_equal(RADIANS_180, Vector2::new(-5.0, 0.0).angle(), 0.0001)); - assert!(nearly_equal(-RADIANS_135, Vector2::new(-5.0, -5.0).angle(), 0.0001)); - assert!(nearly_equal(-RADIANS_90, Vector2::new(0.0, -5.0).angle(), 0.0001)); - assert!(nearly_equal(-RADIANS_45, Vector2::new(5.0, -5.0).angle(), 0.0001)); - - assert!(nearly_equal(crate::math::UP, Vector2::UP.angle(), 0.0001)); - assert!(nearly_equal(crate::math::DOWN, Vector2::DOWN.angle(), 0.0001)); - assert!(nearly_equal(crate::math::LEFT, Vector2::LEFT.angle(), 0.0001)); - assert!(nearly_equal(crate::math::RIGHT, Vector2::RIGHT.angle(), 0.0001)); - } + #[test] + #[rustfmt::skip] + pub fn test_angle() { + assert!(nearly_equal(RADIANS_0, Vector2::new(5.0, 0.0).angle(), 0.0001)); + assert!(nearly_equal(RADIANS_45, Vector2::new(5.0, 5.0).angle(), 0.0001)); + assert!(nearly_equal(RADIANS_90, Vector2::new(0.0, 5.0).angle(), 0.0001)); + assert!(nearly_equal(RADIANS_135, Vector2::new(-5.0, 5.0).angle(), 0.0001)); + assert!(nearly_equal(RADIANS_180, Vector2::new(-5.0, 0.0).angle(), 0.0001)); + assert!(nearly_equal(-RADIANS_135, Vector2::new(-5.0, -5.0).angle(), 0.0001)); + assert!(nearly_equal(-RADIANS_90, Vector2::new(0.0, -5.0).angle(), 0.0001)); + assert!(nearly_equal(-RADIANS_45, Vector2::new(5.0, -5.0).angle(), 0.0001)); - #[test] - pub fn test_from_angle() { - let v = Vector2::from_angle(RADIANS_0); - assert!(nearly_equal(v.x, 1.0, 0.000001)); - assert!(nearly_equal(v.y, 0.0, 0.000001)); - let v = Vector2::from_angle(RADIANS_45); - assert!(nearly_equal(v.x, 0.707106, 0.000001)); - assert!(nearly_equal(v.y, 0.707106, 0.000001)); - let v = Vector2::from_angle(RADIANS_225); - assert!(nearly_equal(v.x, -0.707106, 0.000001)); - assert!(nearly_equal(v.y, -0.707106, 0.000001)); + assert!(nearly_equal(crate::math::UP, Vector2::UP.angle(), 0.0001)); + assert!(nearly_equal(crate::math::DOWN, Vector2::DOWN.angle(), 0.0001)); + assert!(nearly_equal(crate::math::LEFT, Vector2::LEFT.angle(), 0.0001)); + assert!(nearly_equal(crate::math::RIGHT, Vector2::RIGHT.angle(), 0.0001)); + } - let v = Vector2::from_angle(UP); - assert!(v.nearly_equal(Vector2::UP, 0.000001)); - let v = Vector2::from_angle(DOWN); - assert!(v.nearly_equal(Vector2::DOWN, 0.000001)); - let v = Vector2::from_angle(LEFT); - assert!(v.nearly_equal(Vector2::LEFT, 0.000001)); - let v = Vector2::from_angle(RIGHT); - assert!(v.nearly_equal(Vector2::RIGHT, 0.000001)); - } + #[test] + pub fn test_from_angle() { + let v = Vector2::from_angle(RADIANS_0); + assert!(nearly_equal(v.x, 1.0, 0.000001)); + assert!(nearly_equal(v.y, 0.0, 0.000001)); + let v = Vector2::from_angle(RADIANS_45); + assert!(nearly_equal(v.x, 0.707106, 0.000001)); + assert!(nearly_equal(v.y, 0.707106, 0.000001)); + let v = Vector2::from_angle(RADIANS_225); + assert!(nearly_equal(v.x, -0.707106, 0.000001)); + assert!(nearly_equal(v.y, -0.707106, 0.000001)); - #[test] - pub fn test_angle_between() { - let a = Vector2::new(20.0, 20.0); - let b = Vector2::new(10.0, 10.0); - let angle = a.angle_between(&b); - assert!(nearly_equal(-RADIANS_135, angle, 0.0001)); + let v = Vector2::from_angle(UP); + assert!(v.nearly_equal(Vector2::UP, 0.000001)); + let v = Vector2::from_angle(DOWN); + assert!(v.nearly_equal(Vector2::DOWN, 0.000001)); + let v = Vector2::from_angle(LEFT); + assert!(v.nearly_equal(Vector2::LEFT, 0.000001)); + let v = Vector2::from_angle(RIGHT); + assert!(v.nearly_equal(Vector2::RIGHT, 0.000001)); + } - let a = Vector2::new(0.0, 0.0); - let b = Vector2::new(10.0, 10.0); - let angle = a.angle_between(&b); - assert!(nearly_equal(RADIANS_45, angle, 0.0001)); + #[test] + pub fn test_angle_between() { + let a = Vector2::new(20.0, 20.0); + let b = Vector2::new(10.0, 10.0); + let angle = a.angle_between(&b); + assert!(nearly_equal(-RADIANS_135, angle, 0.0001)); - let a = Vector2::new(5.0, 5.0); - let b = Vector2::new(5.0, 5.0); - let angle = a.angle_between(&b); - assert!(nearly_equal(0.0, angle, 0.0001)); - } + let a = Vector2::new(0.0, 0.0); + let b = Vector2::new(10.0, 10.0); + let angle = a.angle_between(&b); + assert!(nearly_equal(RADIANS_45, angle, 0.0001)); - #[test] - pub fn test_lerp() { - let a = Vector2 { x: 5.0, y: 1.0 }; - let b = Vector2 { x: 10.0, y: 2.0 }; - let c = lerp(a, b, 0.5); - assert!(nearly_equal(c.x, 7.5, 0.0001)); - assert!(nearly_equal(c.y, 1.5, 0.0001)); - } + let a = Vector2::new(5.0, 5.0); + let b = Vector2::new(5.0, 5.0); + let angle = a.angle_between(&b); + assert!(nearly_equal(0.0, angle, 0.0001)); + } + + #[test] + pub fn test_lerp() { + let a = Vector2 { x: 5.0, y: 1.0 }; + let b = Vector2 { x: 10.0, y: 2.0 }; + let c = lerp(a, b, 0.5); + assert!(nearly_equal(c.x, 7.5, 0.0001)); + assert!(nearly_equal(c.y, 1.5, 0.0001)); + } } diff --git a/libretrogd/src/states/mod.rs b/libretrogd/src/states/mod.rs index 0a44201..60866e4 100644 --- a/libretrogd/src/states/mod.rs +++ b/libretrogd/src/states/mod.rs @@ -6,1545 +6,1545 @@ use thiserror::Error; #[derive(Copy, Clone, Eq, PartialEq, Debug)] pub enum TransitionTo { - Paused, - Dead, + Paused, + Dead, } #[derive(Copy, Clone, Eq, PartialEq, Debug)] pub enum TransitionDirection { - In, - Out + In, + Out, } #[derive(Copy, Clone, Eq, PartialEq, Debug)] pub enum State { - Pending, - Resume, - Active, - Paused, - TransitionIn, - TransitionOut(TransitionTo), - Dead, + Pending, + Resume, + Active, + Paused, + TransitionIn, + TransitionOut(TransitionTo), + Dead, } /////////////////////////////////////////////////////////////////////////////////////////////////// pub enum StateChange { - Push(Box>), - Swap(Box>), - Pop(u32), + Push(Box>), + Swap(Box>), + Pop(u32), } impl std::fmt::Debug for StateChange { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - use StateChange::*; - match *self { - Push(..) => write!(f, "Push"), - Swap(..) => write!(f, "Swap"), - Pop(n) => write!(f, "Pop({})", n), - } - } + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + use StateChange::*; + match *self { + Push(..) => write!(f, "Push"), + Swap(..) => write!(f, "Swap"), + Pop(n) => write!(f, "Pop({})", n), + } + } } pub trait AppState { - fn update(&mut self, state: State, context: &mut ContextType) -> Option>; - fn render(&mut self, state: State, context: &mut ContextType); - fn transition(&mut self, state: State, context: &mut ContextType) -> bool; - fn state_change(&mut self, new_state: State, old_state: State, context: &mut ContextType); + fn update(&mut self, state: State, context: &mut ContextType) -> Option>; + fn render(&mut self, state: State, context: &mut ContextType); + fn transition(&mut self, state: State, context: &mut ContextType) -> bool; + fn state_change(&mut self, new_state: State, old_state: State, context: &mut ContextType); } /////////////////////////////////////////////////////////////////////////////////////////////////// #[derive(Error, Debug)] pub enum StateError { - #[error("Operation cannot currently be performed because there is already a pending state change.")] - HasPendingStateChange, + #[error("Operation cannot currently be performed because there is already a pending state change.")] + HasPendingStateChange, - #[error("Operation cannot currently be performed because the State's current state ({0:?}) does not allow it.")] - AppStateInvalidState(State), + #[error("Operation cannot currently be performed because the State's current state ({0:?}) does not allow it.")] + AppStateInvalidState(State), } struct StateContainer { - current_state: State, - pending_state_change: Option, - state: Box>, + current_state: State, + pending_state_change: Option, + state: Box>, } impl std::fmt::Debug for StateContainer { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("StateContainer") - .field("current_state", &self.current_state) - .field("pending_state_change", &self.pending_state_change) - .finish_non_exhaustive() - } + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("StateContainer") + .field("current_state", &self.current_state) + .field("pending_state_change", &self.pending_state_change) + .finish_non_exhaustive() + } } impl StateContainer { - pub fn new(state: Box>) -> Self { - StateContainer { - current_state: State::Dead, - pending_state_change: None, - state, - } - } + pub fn new(state: Box>) -> Self { + StateContainer { + current_state: State::Dead, + pending_state_change: None, + state, + } + } - #[inline] - pub fn current_state(&self) -> State { - self.current_state - } + #[inline] + pub fn current_state(&self) -> State { + self.current_state + } - pub fn has_pending_state_change(&self) -> bool { - self.pending_state_change.is_some() - } + pub fn has_pending_state_change(&self) -> bool { + self.pending_state_change.is_some() + } - #[inline] - pub fn pending_state_change(&mut self) -> Option { - self.pending_state_change.take() - } + #[inline] + pub fn pending_state_change(&mut self) -> Option { + self.pending_state_change.take() + } - #[inline] - pub fn change_state(&mut self, new_state: State, context: &mut ContextType) { - let old_state = self.current_state; - self.current_state = new_state; - self.state.state_change(self.current_state, old_state, context); - } + #[inline] + pub fn change_state(&mut self, new_state: State, context: &mut ContextType) { + let old_state = self.current_state; + self.current_state = new_state; + self.state.state_change(self.current_state, old_state, context); + } - #[inline] - pub fn state(&mut self) -> &mut dyn AppState { - self.state.deref_mut() - } + #[inline] + pub fn state(&mut self) -> &mut dyn AppState { + self.state.deref_mut() + } - pub fn transition_out(&mut self, to: TransitionTo, context: &mut ContextType) -> Result<(), StateError> { - if self.current_state == State::Active { - self.change_state(State::TransitionOut(to), context); - Ok(()) - } else { - Err(StateError::AppStateInvalidState(self.current_state)) - } - } + pub fn transition_out(&mut self, to: TransitionTo, context: &mut ContextType) -> Result<(), StateError> { + if self.current_state == State::Active { + self.change_state(State::TransitionOut(to), context); + Ok(()) + } else { + Err(StateError::AppStateInvalidState(self.current_state)) + } + } - #[inline] - pub fn pending_transition_out(&mut self, to: TransitionTo) { - self.pending_state_change = Some(State::TransitionOut(to)); - } + #[inline] + pub fn pending_transition_out(&mut self, to: TransitionTo) { + self.pending_state_change = Some(State::TransitionOut(to)); + } - pub fn transition_in(&mut self, context: &mut ContextType) -> Result<(), StateError> { - match self.current_state { - State::Pending | State::Paused | State::Resume => { - self.change_state(State::TransitionIn, context); - Ok(()) - }, - _ => { - Err(StateError::AppStateInvalidState(self.current_state)) - } - } - } + pub fn transition_in(&mut self, context: &mut ContextType) -> Result<(), StateError> { + match self.current_state { + State::Pending | State::Paused | State::Resume => { + self.change_state(State::TransitionIn, context); + Ok(()) + } + _ => { + Err(StateError::AppStateInvalidState(self.current_state)) + } + } + } - #[allow(dead_code)] - #[inline] - pub fn pending_transition_in(&mut self) { - self.pending_state_change = Some(State::TransitionIn); - } + #[allow(dead_code)] + #[inline] + pub fn pending_transition_in(&mut self) { + self.pending_state_change = Some(State::TransitionIn); + } - pub fn activate(&mut self, context: &mut ContextType) -> Result<(), StateError> { - self.change_state(State::Active, context); - Ok(()) - } + pub fn activate(&mut self, context: &mut ContextType) -> Result<(), StateError> { + self.change_state(State::Active, context); + Ok(()) + } - #[inline] - pub fn pending_activate(&mut self) { - self.pending_state_change = Some(State::Active); - } + #[inline] + pub fn pending_activate(&mut self) { + self.pending_state_change = Some(State::Active); + } - pub fn pause(&mut self, context: &mut ContextType) -> Result<(), StateError> { - self.change_state(State::Paused, context); - Ok(()) - } + pub fn pause(&mut self, context: &mut ContextType) -> Result<(), StateError> { + self.change_state(State::Paused, context); + Ok(()) + } - #[inline] - pub fn pending_pause(&mut self) { - self.pending_state_change = Some(State::Paused); - } + #[inline] + pub fn pending_pause(&mut self) { + self.pending_state_change = Some(State::Paused); + } - pub fn kill(&mut self, context: &mut ContextType) -> Result<(), StateError> { - self.change_state(State::Dead, context); - Ok(()) - } + pub fn kill(&mut self, context: &mut ContextType) -> Result<(), StateError> { + self.change_state(State::Dead, context); + Ok(()) + } - #[inline] - pub fn pending_kill(&mut self) { - self.pending_state_change = Some(State::Dead); - } + #[inline] + pub fn pending_kill(&mut self) { + self.pending_state_change = Some(State::Dead); + } } pub struct States { - states: VecDeque>, - command: Option>, - pending_state: Option>>, - pop_count: Option, + states: VecDeque>, + command: Option>, + pending_state: Option>>, + pop_count: Option, } impl std::fmt::Debug for States { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("States") - .field("states", &self.states) - .field("command", &self.command) - .field("pending_state", match self.pending_state { - Some(..) => &"Some(..)", - None => &"None", - }) - .field("pop_count", &self.pop_count) - .finish_non_exhaustive() - } + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("States") + .field("states", &self.states) + .field("command", &self.command) + .field("pending_state", match self.pending_state { + Some(..) => &"Some(..)", + None => &"None", + }) + .field("pop_count", &self.pop_count) + .finish_non_exhaustive() + } } impl States { - pub fn new() -> Self { - States { - states: VecDeque::new(), - command: None, - pending_state: None, - pop_count: None, - } - } + pub fn new() -> Self { + States { + states: VecDeque::new(), + command: None, + pending_state: None, + pop_count: None, + } + } - #[inline] - pub fn is_empty(&self) -> bool { - self.states.is_empty() && self.pending_state.is_none() && self.command.is_none() - } + #[inline] + pub fn is_empty(&self) -> bool { + self.states.is_empty() && self.pending_state.is_none() && self.command.is_none() + } - fn can_push_or_pop(&self) -> bool { - if let Some(state) = self.states.front() { - if state.current_state != State::Active { - return false; - } - } - if self.pending_state.is_some() { - return false; - } + fn can_push_or_pop(&self) -> bool { + if let Some(state) = self.states.front() { + if state.current_state != State::Active { + return false; + } + } + if self.pending_state.is_some() { + return false; + } - true - } + true + } - fn push_boxed_state(&mut self, boxed_state: Box>) -> Result<(), StateError> { - if !self.can_push_or_pop() { - Err(StateError::HasPendingStateChange) - } else { - self.command = Some(StateChange::Push(boxed_state)); - Ok(()) - } - } + fn push_boxed_state(&mut self, boxed_state: Box>) -> Result<(), StateError> { + if !self.can_push_or_pop() { + Err(StateError::HasPendingStateChange) + } else { + self.command = Some(StateChange::Push(boxed_state)); + Ok(()) + } + } - fn swap_boxed_state(&mut self, boxed_state: Box>) -> Result<(), StateError> { - if !self.can_push_or_pop() { - Err(StateError::HasPendingStateChange) - } else { - self.command = Some(StateChange::Swap(boxed_state)); - Ok(()) - } - } + fn swap_boxed_state(&mut self, boxed_state: Box>) -> Result<(), StateError> { + if !self.can_push_or_pop() { + Err(StateError::HasPendingStateChange) + } else { + self.command = Some(StateChange::Swap(boxed_state)); + Ok(()) + } + } - pub fn push(&mut self, state: impl AppState + 'static) -> Result<(), StateError> { - self.push_boxed_state(Box::new(state)) - } + pub fn push(&mut self, state: impl AppState + 'static) -> Result<(), StateError> { + self.push_boxed_state(Box::new(state)) + } - pub fn swap(&mut self, state: impl AppState + 'static) -> Result<(), StateError> { - self.swap_boxed_state(Box::new(state)) - } + pub fn swap(&mut self, state: impl AppState + 'static) -> Result<(), StateError> { + self.swap_boxed_state(Box::new(state)) + } - pub fn pop(&mut self, count: u32) -> Result<(), StateError> { - if !self.can_push_or_pop() { - Err(StateError::HasPendingStateChange) - } else { - if !self.states.is_empty() { - self.command = Some(StateChange::Pop(count)); - } - Ok(()) - } - } + pub fn pop(&mut self, count: u32) -> Result<(), StateError> { + if !self.can_push_or_pop() { + Err(StateError::HasPendingStateChange) + } else { + if !self.states.is_empty() { + self.command = Some(StateChange::Pop(count)); + } + Ok(()) + } + } - fn state_of_front_state(&self) -> Option { - if let Some(state) = self.states.front() { - Some(state.current_state()) - } else { - None - } - } + fn state_of_front_state(&self) -> Option { + if let Some(state) = self.states.front() { + Some(state.current_state()) + } else { + None + } + } - fn process_state_changes(&mut self, context: &mut ContextType) -> Result<(), StateError> { - // TODO: this function is pretty gross honestly. + fn process_state_changes(&mut self, context: &mut ContextType) -> Result<(), StateError> { + // TODO: this function is pretty gross honestly. - if let Some(command) = self.command.take() { - match command { - StateChange::Push(new_state) => { - self.pending_state = Some(new_state); - }, - StateChange::Pop(count) => { - if let Some(state) = self.states.front_mut() { - state.pending_transition_out(TransitionTo::Dead); - self.pop_count = Some(count); - } - } - StateChange::Swap(new_state) => { - // swap is basically pop+push combined together in one step - if let Some(state) = self.states.front_mut() { - state.pending_transition_out(TransitionTo::Dead); - } - self.pending_state = Some(new_state); - } - } - } + if let Some(command) = self.command.take() { + match command { + StateChange::Push(new_state) => { + self.pending_state = Some(new_state); + } + StateChange::Pop(count) => { + if let Some(state) = self.states.front_mut() { + state.pending_transition_out(TransitionTo::Dead); + self.pop_count = Some(count); + } + } + StateChange::Swap(new_state) => { + // swap is basically pop+push combined together in one step + if let Some(state) = self.states.front_mut() { + state.pending_transition_out(TransitionTo::Dead); + } + self.pending_state = Some(new_state); + } + } + } - if self.pending_state.is_some() { - if self.states.is_empty() { - // special case to bootstrap the stack of states when e.g. the system is first set - // up with the very first state pushed to it. - let mut new_state = StateContainer::new(self.pending_state.take().unwrap()); - new_state.change_state(State::Pending, context); - self.states.push_front(new_state); - } else if self.state_of_front_state() == Some(State::Active) { - // if the current state is active and there is a pending state waiting to be added, - // we need to start transitioning out the active state towards a 'paused' state - let state = self.states.front_mut().unwrap(); - // if this state is being swapped out for another, it will already have a - // pending state change to TransitionOut(Dead) here ... - if !state.has_pending_state_change() { - state.pending_transition_out(TransitionTo::Paused); - } - } - } + if self.pending_state.is_some() { + if self.states.is_empty() { + // special case to bootstrap the stack of states when e.g. the system is first set + // up with the very first state pushed to it. + let mut new_state = StateContainer::new(self.pending_state.take().unwrap()); + new_state.change_state(State::Pending, context); + self.states.push_front(new_state); + } else if self.state_of_front_state() == Some(State::Active) { + // if the current state is active and there is a pending state waiting to be added, + // we need to start transitioning out the active state towards a 'paused' state + let state = self.states.front_mut().unwrap(); + // if this state is being swapped out for another, it will already have a + // pending state change to TransitionOut(Dead) here ... + if !state.has_pending_state_change() { + state.pending_transition_out(TransitionTo::Paused); + } + } + } - // handle any pending state change queued from the previous frame, so that we can - // process the state as necessary below ... - // for some pending state changes, we process them here instead of in the match later on - // in this function so that we're able to transition between old and new states all in - // a single frame. this way we don't have any 'dead' frames where no update/renders get - // run because a state is 'stuck' in a dead or pending state still. - if let Some(state) = self.states.front_mut() { - if let Some(pending_state_change) = state.pending_state_change() { - match pending_state_change { - State::Dead => { - if let Some(pop_count) = self.pop_count { - // pop the requested amount of states off the top - for _ in 0..pop_count { - if let Some(mut state) = self.states.pop_front() { - state.kill(context)?; - } - } - self.pop_count = None; - } else { - // only need to pop off the top state since it is dead, because it - // was swapped out - state.kill(context)?; - self.states.pop_front(); - } + // handle any pending state change queued from the previous frame, so that we can + // process the state as necessary below ... + // for some pending state changes, we process them here instead of in the match later on + // in this function so that we're able to transition between old and new states all in + // a single frame. this way we don't have any 'dead' frames where no update/renders get + // run because a state is 'stuck' in a dead or pending state still. + if let Some(state) = self.states.front_mut() { + if let Some(pending_state_change) = state.pending_state_change() { + match pending_state_change { + State::Dead => { + if let Some(pop_count) = self.pop_count { + // pop the requested amount of states off the top + for _ in 0..pop_count { + if let Some(mut state) = self.states.pop_front() { + state.kill(context)?; + } + } + self.pop_count = None; + } else { + // only need to pop off the top state since it is dead, because it + // was swapped out + state.kill(context)?; + self.states.pop_front(); + } - if self.pending_state.is_some() { - // if there is a new pending state waiting, we can add it here right now - let mut new_state = StateContainer::new(self.pending_state.take().unwrap()); - new_state.change_state(State::Pending, context); - self.states.push_front(new_state); - } else if self.state_of_front_state() == Some(State::Paused) { - // otherwise, we're probably waking up a state that was paused and needs to - // be resumed since it's once again on top - let state = self.states.front_mut().unwrap(); - state.change_state(State::Resume, context); - state.transition_in(context)?; - } - }, - State::Paused => { - state.pause(context)?; + if self.pending_state.is_some() { + // if there is a new pending state waiting, we can add it here right now + let mut new_state = StateContainer::new(self.pending_state.take().unwrap()); + new_state.change_state(State::Pending, context); + self.states.push_front(new_state); + } else if self.state_of_front_state() == Some(State::Paused) { + // otherwise, we're probably waking up a state that was paused and needs to + // be resumed since it's once again on top + let state = self.states.front_mut().unwrap(); + state.change_state(State::Resume, context); + state.transition_in(context)?; + } + } + State::Paused => { + state.pause(context)?; - if self.pending_state.is_some() { - // top state is paused and we have a new state waiting to be added. - // add the new state - let mut new_state = StateContainer::new(self.pending_state.take().unwrap()); - new_state.change_state(State::Pending, context); - self.states.push_front(new_state); - } - }, - State::Active => state.activate(context)?, - State::TransitionOut(to) => state.transition_out(to, context)?, - State::TransitionIn => state.transition_in(context)?, - _ => {}, - } - } - } + if self.pending_state.is_some() { + // top state is paused and we have a new state waiting to be added. + // add the new state + let mut new_state = StateContainer::new(self.pending_state.take().unwrap()); + new_state.change_state(State::Pending, context); + self.states.push_front(new_state); + } + } + State::Active => state.activate(context)?, + State::TransitionOut(to) => state.transition_out(to, context)?, + State::TransitionIn => state.transition_in(context)?, + _ => {} + } + } + } - // special case, switch pending state into transition right away so we don't lose a frame - if self.state_of_front_state() == Some(State::Pending) { - // top state is just sitting there pending, lets start it up ... - let state = self.states.front_mut().unwrap(); - state.transition_in(context)?; - } + // special case, switch pending state into transition right away so we don't lose a frame + if self.state_of_front_state() == Some(State::Pending) { + // top state is just sitting there pending, lets start it up ... + let state = self.states.front_mut().unwrap(); + state.transition_in(context)?; + } - // now figure out what state change processing is needed based on the current state ... - match self.state_of_front_state() { - Some(State::Paused) => { - // should never happen now. leaving here just in case ... - return Err(StateError::AppStateInvalidState(State::Paused)); - }, - Some(State::Dead) => { - // should never happen now. leaving here just in case ... - return Err(StateError::AppStateInvalidState(State::Dead)); - }, - Some(State::TransitionIn) => { - let state = self.states.front_mut().unwrap(); - if state.state().transition(State::TransitionIn, context) { - // state has indicated it is done transitioning, so we can switch it to active - state.pending_activate(); - } - }, - Some(State::TransitionOut(to)) => { - let state = self.states.front_mut().unwrap(); - if state.state().transition(State::TransitionOut(to), context) { - // state has indicated it is done transitioning, so we can switch it to whatever - // it was transitioning to - match to { - TransitionTo::Paused => { state.pending_pause(); }, - TransitionTo::Dead => { state.pending_kill(); } - } - } - }, - _ => {} - } + // now figure out what state change processing is needed based on the current state ... + match self.state_of_front_state() { + Some(State::Paused) => { + // should never happen now. leaving here just in case ... + return Err(StateError::AppStateInvalidState(State::Paused)); + } + Some(State::Dead) => { + // should never happen now. leaving here just in case ... + return Err(StateError::AppStateInvalidState(State::Dead)); + } + Some(State::TransitionIn) => { + let state = self.states.front_mut().unwrap(); + if state.state().transition(State::TransitionIn, context) { + // state has indicated it is done transitioning, so we can switch it to active + state.pending_activate(); + } + } + Some(State::TransitionOut(to)) => { + let state = self.states.front_mut().unwrap(); + if state.state().transition(State::TransitionOut(to), context) { + // state has indicated it is done transitioning, so we can switch it to whatever + // it was transitioning to + match to { + TransitionTo::Paused => { state.pending_pause(); } + TransitionTo::Dead => { state.pending_kill(); } + } + } + } + _ => {} + } - Ok(()) - } + Ok(()) + } - pub fn update(&mut self, context: &mut ContextType) -> Result<(), StateError> { - self.process_state_changes(context)?; - if let Some(state) = self.states.front_mut() { - let current_state = state.current_state(); - match current_state { - State::Active | State::TransitionIn | State::TransitionOut(_) => { - if let Some(state_change) = state.state().update(current_state, context) { - match state_change { - StateChange::Push(state) => self.push_boxed_state(state)?, - StateChange::Swap(state) => self.swap_boxed_state(state)?, - StateChange::Pop(count) => self.pop(count)?, - } - } - } - _ => {} - } - } - Ok(()) - } + pub fn update(&mut self, context: &mut ContextType) -> Result<(), StateError> { + self.process_state_changes(context)?; + if let Some(state) = self.states.front_mut() { + let current_state = state.current_state(); + match current_state { + State::Active | State::TransitionIn | State::TransitionOut(_) => { + if let Some(state_change) = state.state().update(current_state, context) { + match state_change { + StateChange::Push(state) => self.push_boxed_state(state)?, + StateChange::Swap(state) => self.swap_boxed_state(state)?, + StateChange::Pop(count) => self.pop(count)?, + } + } + } + _ => {} + } + } + Ok(()) + } - pub fn render(&mut self, context: &mut ContextType) { - if let Some(state) = self.states.front_mut() { - let current_state = state.current_state(); - match current_state { - State::Active | State::TransitionIn | State::TransitionOut(_) => { - state.state().render(current_state, context); - }, - _ => {} - } - } - } + pub fn render(&mut self, context: &mut ContextType) { + if let Some(state) = self.states.front_mut() { + let current_state = state.current_state(); + match current_state { + State::Active | State::TransitionIn | State::TransitionOut(_) => { + state.state().render(current_state, context); + } + _ => {} + } + } + } } #[cfg(test)] mod tests { - use claim::*; - - use super::*; - - #[derive(Debug, Eq, PartialEq, Copy, Clone)] - enum LogEntry { - Update(u32, State), - Render(u32, State), - Transition(u32, State), - StateChange(u32, State, State), - } - - struct TestContext { - pub log: Vec, - } - - impl TestContext { - pub fn new() -> Self { - TestContext { - log: Vec::new() - } - } - - pub fn log(&mut self, entry: LogEntry) { - self.log.push(entry); - } - - pub fn take_log(&mut self) -> Vec { - let taken = self.log.to_vec(); - self.log.clear(); - taken - } - } - - struct TestState { - id: u32, - counter: u32, - transition_length: u32, - } - - impl TestState { - pub fn new(id: u32) -> Self { - TestState { - id, - counter: 0, - transition_length: 0, - } - } - - pub fn new_with_transition_length(id: u32, transition_length: u32) -> Self { - TestState { - id, - counter: 0, - transition_length, - } - } - } - - impl AppState for TestState { - fn update(&mut self, state: State, context: &mut TestContext) -> Option> { - context.log(LogEntry::Update(self.id, state)); - None - } - - fn render(&mut self, state: State, context: &mut TestContext) { - context.log(LogEntry::Render(self.id, state)); - } - - fn transition(&mut self, state: State, context: &mut TestContext) -> bool { - context.log(LogEntry::Transition(self.id, state)); - if self.counter > 0 { - self.counter -= 1; - } - if self.counter == 0 { - true - } else { - false - } - } - - fn state_change(&mut self, new_state: State, old_state: State, context: &mut TestContext) { - context.log(LogEntry::StateChange(self.id, new_state, old_state)); - match new_state { - State::TransitionIn | State::TransitionOut(_) => { - self.counter = self.transition_length; - }, - _ => {} - } - } - } - - fn tick(states: &mut States, context: &mut ContextType) -> Result<(), StateError> { - states.update(context)?; - states.render(context); - Ok(()) - } - - #[test] - fn push_and_pop_state() -> Result<(), StateError> { - use LogEntry::*; - use State::*; - - const FOO: u32 = 1; - - let mut states = States::::new(); - let mut context = TestContext::new(); - - states.push(TestState::new(FOO))?; - assert_eq!(context.take_log(), vec![]); - - // state will transition in - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(FOO, Pending, Dead), - StateChange(FOO, TransitionIn, Pending), - Transition(FOO, TransitionIn), - Update(FOO, TransitionIn), - Render(FOO, TransitionIn) - ] - ); - // state finished transitioning in, now moves to active - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(FOO, Active, TransitionIn), - Update(FOO, Active), - Render(FOO, Active) - ] - ); - - states.pop(1)?; - assert_eq!(context.take_log(), vec![]); - - // state begins to transition out to 'dead' - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(FOO, TransitionOut(TransitionTo::Dead), Active), - Transition(FOO, TransitionOut(TransitionTo::Dead)), - Update(FOO, TransitionOut(TransitionTo::Dead)), - Render(FOO, TransitionOut(TransitionTo::Dead)) - ] - ); - // state finished transitioning out, now dies - tick(&mut states, &mut context)?; - assert_eq!(context.take_log(), vec![StateChange(FOO, Dead, TransitionOut(TransitionTo::Dead))]); - - // nothing! no states anymore! - tick(&mut states, &mut context)?; - assert_eq!(context.take_log(), vec![]); - - Ok(()) - } - - #[test] - fn push_and_pop_state_with_longer_transition() -> Result<(), StateError> { - use LogEntry::*; - use State::*; - - const FOO: u32 = 1; - - let mut states = States::::new(); - let mut context = TestContext::new(); - - states.push(TestState::new_with_transition_length(FOO, 5))?; - assert_eq!(context.take_log(), vec![]); - - // state will transition in - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(FOO, Pending, Dead), - StateChange(FOO, TransitionIn, Pending), - Transition(FOO, TransitionIn), - Update(FOO, TransitionIn), - Render(FOO, TransitionIn) - ] - ); - // wait for transition to finish - for _ in 0..4 { - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - Transition(FOO, TransitionIn), - Update(FOO, TransitionIn), - Render(FOO, TransitionIn) - ] - ); - } - // state finished transitioning in, now moves to active - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(FOO, Active, TransitionIn), - Update(FOO, Active), - Render(FOO, Active) - ] - ); - - states.pop(1)?; - assert_eq!(context.take_log(), vec![]); - - // state begins to transition out to 'dead' - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(FOO, TransitionOut(TransitionTo::Dead), Active), - Transition(FOO, TransitionOut(TransitionTo::Dead)), - Update(FOO, TransitionOut(TransitionTo::Dead)), - Render(FOO, TransitionOut(TransitionTo::Dead)) - ] - ); - // wait for transition to finish - for _ in 0..4 { - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - Transition(FOO, TransitionOut(TransitionTo::Dead)), - Update(FOO, TransitionOut(TransitionTo::Dead)), - Render(FOO, TransitionOut(TransitionTo::Dead)) - ] - ); - } - // state finished transitioning out, now dies - tick(&mut states, &mut context)?; - assert_eq!(context.take_log(), vec![StateChange(FOO, Dead, TransitionOut(TransitionTo::Dead))]); - - // nothing! no states anymore! - tick(&mut states, &mut context)?; - assert_eq!(context.take_log(), vec![]); - - Ok(()) - } - - #[test] - fn push_and_pop_multiple_states() -> Result<(), StateError> { - use LogEntry::*; - use State::*; - - const FIRST: u32 = 1; - const SECOND: u32 = 2; - - let mut states = States::::new(); - let mut context = TestContext::new(); - - // push first state - states.push(TestState::new(FIRST))?; - assert_eq!(context.take_log(), vec![]); - - // first state will transition in - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(FIRST, Pending, Dead), - StateChange(FIRST, TransitionIn, Pending), - Transition(FIRST, TransitionIn), - Update(FIRST, TransitionIn), - Render(FIRST, TransitionIn) - ] - ); - // first state finished transitioning in, now moves to active - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(FIRST, Active, TransitionIn), - Update(FIRST, Active), - Render(FIRST, Active) - ] - ); - - // push second state - states.push(TestState::new(SECOND))?; - assert_eq!(context.take_log(), vec![]); - - // first state begins to transition out to 'paused' state - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(FIRST, TransitionOut(TransitionTo::Paused), Active), - Transition(FIRST, TransitionOut(TransitionTo::Paused)), - Update(FIRST, TransitionOut(TransitionTo::Paused)), - Render(FIRST, TransitionOut(TransitionTo::Paused)) - ] - ); - // state finished transitioning out, now is paused - // second state starts up, will transition in - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(FIRST, Paused, TransitionOut(TransitionTo::Paused)), - StateChange(SECOND, Pending, Dead), - StateChange(SECOND, TransitionIn, Pending), - Transition(SECOND, TransitionIn), - Update(SECOND, TransitionIn), - Render(SECOND, TransitionIn) - ] - ); - // second state finished transitioning in, now moves to active - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(SECOND, Active, TransitionIn), - Update(SECOND, Active), - Render(SECOND, Active) - ] - ); - - // pop second state - states.pop(1)?; - assert_eq!(context.take_log(), vec![]); - - // second state begins to transition out to 'dead' - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(SECOND, TransitionOut(TransitionTo::Dead), Active), - Transition(SECOND, TransitionOut(TransitionTo::Dead)), - Update(SECOND, TransitionOut(TransitionTo::Dead)), - Render(SECOND, TransitionOut(TransitionTo::Dead)) - ] - ); - // second state finished transitioning out, now dies. first state wakes up again and - // starts to transition back in - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(SECOND, Dead, TransitionOut(TransitionTo::Dead)), - StateChange(FIRST, Resume, Paused), - StateChange(FIRST, TransitionIn, Resume), - Transition(FIRST, TransitionIn), - Update(FIRST, TransitionIn), - Render(FIRST, TransitionIn) - ] - ); - // first state finished transitioning in, now moves to active - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(FIRST, Active, TransitionIn), - Update(FIRST, Active), - Render(FIRST, Active) - ] - ); - - // pop first state - states.pop(1)?; - assert_eq!(context.take_log(), vec![]); - - // first state begins to transition out to 'dead' - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(FIRST, TransitionOut(TransitionTo::Dead), Active), - Transition(FIRST, TransitionOut(TransitionTo::Dead)), - Update(FIRST, TransitionOut(TransitionTo::Dead)), - Render(FIRST, TransitionOut(TransitionTo::Dead)) - ] - ); - // first state finished transitioning out, now dies - tick(&mut states, &mut context)?; - assert_eq!(context.take_log(), vec![StateChange(FIRST, Dead, TransitionOut(TransitionTo::Dead))]); - - // nothing! no states anymore! - tick(&mut states, &mut context)?; - assert_eq!(context.take_log(), vec![]); - - Ok(()) - } - - #[test] - fn push_and_pop_multiple_states_with_longer_transitions() -> Result<(), StateError> { - use LogEntry::*; - use State::*; - - const FIRST: u32 = 1; - const SECOND: u32 = 2; - - let mut states = States::::new(); - let mut context = TestContext::new(); - - // push first state - states.push(TestState::new_with_transition_length(FIRST, 3))?; - assert_eq!(context.take_log(), vec![]); - - // first state will transition in - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(FIRST, Pending, Dead), - StateChange(FIRST, TransitionIn, Pending), - Transition(FIRST, TransitionIn), - Update(FIRST, TransitionIn), - Render(FIRST, TransitionIn) - ] - ); - // wait for transition to finish - for _ in 0..2 { - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - Transition(FIRST, TransitionIn), - Update(FIRST, TransitionIn), - Render(FIRST, TransitionIn) - ] - ); - } - // first state finished transitioning in, now moves to active - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(FIRST, Active, TransitionIn), - Update(FIRST, Active), - Render(FIRST, Active) - ] - ); - - // push second state - - states.push(TestState::new_with_transition_length(SECOND, 5))?; - assert_eq!(context.take_log(), vec![]); - - // first state begins to transition out to 'paused' state - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(FIRST, TransitionOut(TransitionTo::Paused), Active), - Transition(FIRST, TransitionOut(TransitionTo::Paused)), - Update(FIRST, TransitionOut(TransitionTo::Paused)), - Render(FIRST, TransitionOut(TransitionTo::Paused)) - ] - ); - // wait for transition to finish - for _ in 0..2 { - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - Transition(FIRST, TransitionOut(TransitionTo::Paused)), - Update(FIRST, TransitionOut(TransitionTo::Paused)), - Render(FIRST, TransitionOut(TransitionTo::Paused)) - ] - ); - } - // first state finished transitioning out, now is paused. second state will transition in - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(FIRST, Paused, TransitionOut(TransitionTo::Paused)), - StateChange(SECOND, Pending, Dead), - StateChange(SECOND, TransitionIn, Pending), - Transition(SECOND, TransitionIn), - Update(SECOND, TransitionIn), - Render(SECOND, TransitionIn) - ] - ); - // wait for transition to finish - for _ in 0..4 { - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - Transition(SECOND, TransitionIn), - Update(SECOND, TransitionIn), - Render(SECOND, TransitionIn) - ] - ); - } - // second state finished transitioning in, now moves to active - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(SECOND, Active, TransitionIn), - Update(SECOND, Active), - Render(SECOND, Active) - ] - ); - - // pop second state - states.pop(1)?; - assert_eq!(context.take_log(), vec![]); - - // second state begins to transition out to 'dead' - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(SECOND, TransitionOut(TransitionTo::Dead), Active), - Transition(SECOND, TransitionOut(TransitionTo::Dead)), - Update(SECOND, TransitionOut(TransitionTo::Dead)), - Render(SECOND, TransitionOut(TransitionTo::Dead)) - ] - ); - // wait for transition to finish - for _ in 0..4 { - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - Transition(SECOND, TransitionOut(TransitionTo::Dead)), - Update(SECOND, TransitionOut(TransitionTo::Dead)), - Render(SECOND, TransitionOut(TransitionTo::Dead)) - ] - ); - } - // second state finished transitioning out, now dies. first state wakes up again and - // starts to transition back in - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(SECOND, Dead, TransitionOut(TransitionTo::Dead)), - StateChange(FIRST, Resume, Paused), - StateChange(FIRST, TransitionIn, Resume), - Transition(FIRST, TransitionIn), - Update(FIRST, TransitionIn), - Render(FIRST, TransitionIn) - ] - ); - // wait for transition to finish - for _ in 0..2 { - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - Transition(FIRST, TransitionIn), - Update(FIRST, TransitionIn), - Render(FIRST, TransitionIn) - ] - ); - } - // first state finished transitioning in, now moves to active - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(FIRST, Active, TransitionIn), - Update(FIRST, Active), - Render(FIRST, Active) - ] - ); - - // pop first state - states.pop(1)?; - assert_eq!(context.take_log(), vec![]); - - // first state begins to transition out to 'dead' - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(FIRST, TransitionOut(TransitionTo::Dead), Active), - Transition(FIRST, TransitionOut(TransitionTo::Dead)), - Update(FIRST, TransitionOut(TransitionTo::Dead)), - Render(FIRST, TransitionOut(TransitionTo::Dead)) - ] - ); - // wait for transition to finish - for _ in 0..2 { - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - Transition(FIRST, TransitionOut(TransitionTo::Dead)), - Update(FIRST, TransitionOut(TransitionTo::Dead)), - Render(FIRST, TransitionOut(TransitionTo::Dead)) - ] - ); - } - // first state finished transitioning out, now dies - tick(&mut states, &mut context)?; - assert_eq!(context.take_log(), vec![StateChange(FIRST, Dead, TransitionOut(TransitionTo::Dead))]); - - // nothing! no states anymore! - tick(&mut states, &mut context)?; - assert_eq!(context.take_log(), vec![]); - - Ok(()) - } - - #[test] - fn pop_multiple_states() -> Result<(), StateError> { - use LogEntry::*; - use State::*; - - const FIRST: u32 = 1; - const SECOND: u32 = 2; - - let mut states = States::::new(); - let mut context = TestContext::new(); - - // push first state - states.push(TestState::new(FIRST))?; - assert_eq!(context.take_log(), vec![]); - - // first state will transition in - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(FIRST, Pending, Dead), - StateChange(FIRST, TransitionIn, Pending), - Transition(FIRST, TransitionIn), - Update(FIRST, TransitionIn), - Render(FIRST, TransitionIn) - ] - ); - // first state finished transitioning in, now moves to active - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(FIRST, Active, TransitionIn), - Update(FIRST, Active), - Render(FIRST, Active) - ] - ); - - // push second state - states.push(TestState::new(SECOND))?; - assert_eq!(context.take_log(), vec![]); - - // first state begins to transition out to 'paused' state - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(FIRST, TransitionOut(TransitionTo::Paused), Active), - Transition(FIRST, TransitionOut(TransitionTo::Paused)), - Update(FIRST, TransitionOut(TransitionTo::Paused)), - Render(FIRST, TransitionOut(TransitionTo::Paused)) - ] - ); - // state finished transitioning out, now is paused - // second state starts up, will transition in - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(FIRST, Paused, TransitionOut(TransitionTo::Paused)), - StateChange(SECOND, Pending, Dead), - StateChange(SECOND, TransitionIn, Pending), - Transition(SECOND, TransitionIn), - Update(SECOND, TransitionIn), - Render(SECOND, TransitionIn) - ] - ); - // second state finished transitioning in, now moves to active - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(SECOND, Active, TransitionIn), - Update(SECOND, Active), - Render(SECOND, Active) - ] - ); - - // pop both states - states.pop(2)?; - assert_eq!(context.take_log(), vec![]); - - // second state begins to transition out to 'dead' - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(SECOND, TransitionOut(TransitionTo::Dead), Active), - Transition(SECOND, TransitionOut(TransitionTo::Dead)), - Update(SECOND, TransitionOut(TransitionTo::Dead)), - Render(SECOND, TransitionOut(TransitionTo::Dead)) - ] - ); - // second state finished transitioning out, now dies. - // first state only goes through a state change, paused to dead. no transition - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(SECOND, Dead, TransitionOut(TransitionTo::Dead)), - StateChange(FIRST, Dead, Paused) - ] - ); - - // nothing! no states anymore! - tick(&mut states, &mut context)?; - assert_eq!(context.take_log(), vec![]); - - Ok(()) - } - - - #[test] - fn swap_states() -> Result<(), StateError> { - use LogEntry::*; - use State::*; - - const FIRST: u32 = 1; - const SECOND: u32 = 2; - - let mut states = States::::new(); - let mut context = TestContext::new(); - - // push first state - states.push(TestState::new(FIRST))?; - assert_eq!(context.take_log(), vec![]); - - // first state will transition in - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(FIRST, Pending, Dead), - StateChange(FIRST, TransitionIn, Pending), - Transition(FIRST, TransitionIn), - Update(FIRST, TransitionIn), - Render(FIRST, TransitionIn) - ] - ); - // first state finished transitioning in, now moves to active - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(FIRST, Active, TransitionIn), - Update(FIRST, Active), - Render(FIRST, Active) - ] - ); - - // swap in second state - states.swap(TestState::new(SECOND))?; - assert_eq!(context.take_log(), vec![]); - - // first state begins to transition out to 'dead' - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(FIRST, TransitionOut(TransitionTo::Dead), Active), - Transition(FIRST, TransitionOut(TransitionTo::Dead)), - Update(FIRST, TransitionOut(TransitionTo::Dead)), - Render(FIRST, TransitionOut(TransitionTo::Dead)) - ] - ); - // first state finished transitioning out, now dies. - // second state starts up, will transition in - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(FIRST, Dead, TransitionOut(TransitionTo::Dead)), - StateChange(SECOND, Pending, Dead), - StateChange(SECOND, TransitionIn, Pending), - Transition(SECOND, TransitionIn), - Update(SECOND, TransitionIn), - Render(SECOND, TransitionIn) - ] - ); - // second state finished transitioning in, now moves to active - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(SECOND, Active, TransitionIn), - Update(SECOND, Active), - Render(SECOND, Active) - ] - ); - - states.pop(1)?; - assert_eq!(context.take_log(), vec![]); - - // state begins to transition out to 'dead' - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(SECOND, TransitionOut(TransitionTo::Dead), Active), - Transition(SECOND, TransitionOut(TransitionTo::Dead)), - Update(SECOND, TransitionOut(TransitionTo::Dead)), - Render(SECOND, TransitionOut(TransitionTo::Dead)) - ] - ); - // state finished transitioning out, now dies - tick(&mut states, &mut context)?; - assert_eq!(context.take_log(), vec![StateChange(SECOND, Dead, TransitionOut(TransitionTo::Dead))]); - - // nothing! no states anymore! - tick(&mut states, &mut context)?; - assert_eq!(context.take_log(), vec![]); - - Ok(()) - } - - struct SelfPushPopState { - id: u32, - counter: u32, - push_after: Option, - pop_after: u32, - } - - impl SelfPushPopState { - pub fn new(id: u32, push_after: Option, pop_after: u32) -> Self { - SelfPushPopState { - id, - counter: 0, - push_after, - pop_after - } - } - } - - impl AppState for SelfPushPopState { - fn update(&mut self, state: State, context: &mut TestContext) -> Option> { - context.log(LogEntry::Update(self.id, state)); - if state == State::Active { - self.counter += 1; - if self.push_after == Some(self.counter) { - return Some(StateChange::Push(Box::new(SelfPushPopState::new(self.id + 1, None, self.pop_after)))); - } else if self.pop_after == self.counter { - return Some(StateChange::Pop(1)); - } - } - None - } - - fn render(&mut self, state: State, context: &mut TestContext) { - context.log(LogEntry::Render(self.id, state)); - } - - fn transition(&mut self, state: State, context: &mut TestContext) -> bool { - context.log(LogEntry::Transition(self.id, state)); - true - } - - fn state_change(&mut self, new_state: State, old_state: State, context: &mut TestContext) { - context.log(LogEntry::StateChange(self.id, new_state, old_state)); - } - } - - - #[test] - fn state_can_push_and_pop_states_itself() -> Result<(), StateError> { - use LogEntry::*; - use State::*; - - const FIRST: u32 = 1; - const SECOND: u32 = 2; - - let mut states = States::::new(); - let mut context = TestContext::new(); - - // push first state. it will do the rest this time ... - states.push(SelfPushPopState::new(FIRST, Some(5), 10))?; - assert_eq!(context.take_log(), vec![]); - - // first state will transition in - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(FIRST, Pending, Dead), - StateChange(FIRST, TransitionIn, Pending), - Transition(FIRST, TransitionIn), - Update(FIRST, TransitionIn), - Render(FIRST, TransitionIn) - ] - ); - // first state finished transitioning in, now moves to active - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(FIRST, Active, TransitionIn), - Update(FIRST, Active), - Render(FIRST, Active) - ] - ); - // wait for first state's counter to count up to where it should push the second state - for _ in 0..4 { - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - Update(FIRST, Active), - Render(FIRST, Active) - ] - ); - } - - // first state begins to transition out to 'paused' state because it pushed the second state - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(FIRST, TransitionOut(TransitionTo::Paused), Active), - Transition(FIRST, TransitionOut(TransitionTo::Paused)), - Update(FIRST, TransitionOut(TransitionTo::Paused)), - Render(FIRST, TransitionOut(TransitionTo::Paused)) - ] - ); - // first state finished transitioning out, now is paused. second state will transition in - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(FIRST, Paused, TransitionOut(TransitionTo::Paused)), - StateChange(SECOND, Pending, Dead), - StateChange(SECOND, TransitionIn, Pending), - Transition(SECOND, TransitionIn), - Update(SECOND, TransitionIn), - Render(SECOND, TransitionIn) - ] - ); - // second state finished transitioning in, now moves to active - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(SECOND, Active, TransitionIn), - Update(SECOND, Active), - Render(SECOND, Active) - ] - ); - // wait for second state's counter to count up to where it should pop itself - for _ in 0..9 { - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - Update(SECOND, Active), - Render(SECOND, Active) - ] - ); - } - - // second state begins to transition out to 'dead' - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(SECOND, TransitionOut(TransitionTo::Dead), Active), - Transition(SECOND, TransitionOut(TransitionTo::Dead)), - Update(SECOND, TransitionOut(TransitionTo::Dead)), - Render(SECOND, TransitionOut(TransitionTo::Dead)) - ] - ); - // second state finished transitioning out, now dies. first state wakes up again and - // starts to transition back in - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(SECOND, Dead, TransitionOut(TransitionTo::Dead)), - StateChange(FIRST, Resume, Paused), - StateChange(FIRST, TransitionIn, Resume), - Transition(FIRST, TransitionIn), - Update(FIRST, TransitionIn), - Render(FIRST, TransitionIn) - ] - ); - // first state finished transitioning in, now moves to active - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(FIRST, Active, TransitionIn), - Update(FIRST, Active), - Render(FIRST, Active) - ] - ); - // wait for first state's counter to count up to where it should pop itself - for _ in 0..4 { - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - Update(FIRST, Active), - Render(FIRST, Active) - ] - ); - } - - // first state begins to transition out to 'dead' - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(FIRST, TransitionOut(TransitionTo::Dead), Active), - Transition(FIRST, TransitionOut(TransitionTo::Dead)), - Update(FIRST, TransitionOut(TransitionTo::Dead)), - Render(FIRST, TransitionOut(TransitionTo::Dead)) - ] - ); - // first state finished transitioning out, now dies - tick(&mut states, &mut context)?; - assert_eq!(context.take_log(), vec![StateChange(FIRST, Dead, TransitionOut(TransitionTo::Dead))]); - - // nothing! no states anymore! - tick(&mut states, &mut context)?; - assert_eq!(context.take_log(), vec![]); - - Ok(()) - } - - #[test] - fn cannot_push_or_pop_states_when_current_state_not_active() -> Result<(), StateError> { - use LogEntry::*; - use State::*; - - const FOO: u32 = 1; - - let mut states = States::::new(); - let mut context = TestContext::new(); - - states.push(TestState::new(FOO))?; - assert_eq!(context.take_log(), vec![]); - - // state will transition in - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(FOO, Pending, Dead), - StateChange(FOO, TransitionIn, Pending), - Transition(FOO, TransitionIn), - Update(FOO, TransitionIn), - Render(FOO, TransitionIn) - ] - ); - - assert_matches!(states.push(TestState::new(123)), Err(StateError::HasPendingStateChange)); - assert_matches!(states.pop(1), Err(StateError::HasPendingStateChange)); - - // state finished transitioning in, now moves to active - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(FOO, Active, TransitionIn), - Update(FOO, Active), - Render(FOO, Active) - ] - ); - - states.pop(1)?; - assert_eq!(context.take_log(), vec![]); - - // state begins to transition out to 'dead' - tick(&mut states, &mut context)?; - assert_eq!( - context.take_log(), - vec![ - StateChange(FOO, TransitionOut(TransitionTo::Dead), Active), - Transition(FOO, TransitionOut(TransitionTo::Dead)), - Update(FOO, TransitionOut(TransitionTo::Dead)), - Render(FOO, TransitionOut(TransitionTo::Dead)) - ] - ); - - assert_matches!(states.push(TestState::new(123)), Err(StateError::HasPendingStateChange)); - assert_matches!(states.pop(1), Err(StateError::HasPendingStateChange)); - - // state finished transitioning out, now dies - tick(&mut states, &mut context)?; - assert_eq!(context.take_log(), vec![StateChange(FOO, Dead, TransitionOut(TransitionTo::Dead))]); - - states.pop(1)?; - - // nothing! no states anymore! - tick(&mut states, &mut context)?; - assert_eq!(context.take_log(), vec![]); - - Ok(()) - } + use claim::*; + + use super::*; + + #[derive(Debug, Eq, PartialEq, Copy, Clone)] + enum LogEntry { + Update(u32, State), + Render(u32, State), + Transition(u32, State), + StateChange(u32, State, State), + } + + struct TestContext { + pub log: Vec, + } + + impl TestContext { + pub fn new() -> Self { + TestContext { + log: Vec::new() + } + } + + pub fn log(&mut self, entry: LogEntry) { + self.log.push(entry); + } + + pub fn take_log(&mut self) -> Vec { + let taken = self.log.to_vec(); + self.log.clear(); + taken + } + } + + struct TestState { + id: u32, + counter: u32, + transition_length: u32, + } + + impl TestState { + pub fn new(id: u32) -> Self { + TestState { + id, + counter: 0, + transition_length: 0, + } + } + + pub fn new_with_transition_length(id: u32, transition_length: u32) -> Self { + TestState { + id, + counter: 0, + transition_length, + } + } + } + + impl AppState for TestState { + fn update(&mut self, state: State, context: &mut TestContext) -> Option> { + context.log(LogEntry::Update(self.id, state)); + None + } + + fn render(&mut self, state: State, context: &mut TestContext) { + context.log(LogEntry::Render(self.id, state)); + } + + fn transition(&mut self, state: State, context: &mut TestContext) -> bool { + context.log(LogEntry::Transition(self.id, state)); + if self.counter > 0 { + self.counter -= 1; + } + if self.counter == 0 { + true + } else { + false + } + } + + fn state_change(&mut self, new_state: State, old_state: State, context: &mut TestContext) { + context.log(LogEntry::StateChange(self.id, new_state, old_state)); + match new_state { + State::TransitionIn | State::TransitionOut(_) => { + self.counter = self.transition_length; + } + _ => {} + } + } + } + + fn tick(states: &mut States, context: &mut ContextType) -> Result<(), StateError> { + states.update(context)?; + states.render(context); + Ok(()) + } + + #[test] + fn push_and_pop_state() -> Result<(), StateError> { + use LogEntry::*; + use State::*; + + const FOO: u32 = 1; + + let mut states = States::::new(); + let mut context = TestContext::new(); + + states.push(TestState::new(FOO))?; + assert_eq!(context.take_log(), vec![]); + + // state will transition in + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(FOO, Pending, Dead), + StateChange(FOO, TransitionIn, Pending), + Transition(FOO, TransitionIn), + Update(FOO, TransitionIn), + Render(FOO, TransitionIn), + ] + ); + // state finished transitioning in, now moves to active + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(FOO, Active, TransitionIn), + Update(FOO, Active), + Render(FOO, Active), + ] + ); + + states.pop(1)?; + assert_eq!(context.take_log(), vec![]); + + // state begins to transition out to 'dead' + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(FOO, TransitionOut(TransitionTo::Dead), Active), + Transition(FOO, TransitionOut(TransitionTo::Dead)), + Update(FOO, TransitionOut(TransitionTo::Dead)), + Render(FOO, TransitionOut(TransitionTo::Dead)), + ] + ); + // state finished transitioning out, now dies + tick(&mut states, &mut context)?; + assert_eq!(context.take_log(), vec![StateChange(FOO, Dead, TransitionOut(TransitionTo::Dead))]); + + // nothing! no states anymore! + tick(&mut states, &mut context)?; + assert_eq!(context.take_log(), vec![]); + + Ok(()) + } + + #[test] + fn push_and_pop_state_with_longer_transition() -> Result<(), StateError> { + use LogEntry::*; + use State::*; + + const FOO: u32 = 1; + + let mut states = States::::new(); + let mut context = TestContext::new(); + + states.push(TestState::new_with_transition_length(FOO, 5))?; + assert_eq!(context.take_log(), vec![]); + + // state will transition in + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(FOO, Pending, Dead), + StateChange(FOO, TransitionIn, Pending), + Transition(FOO, TransitionIn), + Update(FOO, TransitionIn), + Render(FOO, TransitionIn), + ] + ); + // wait for transition to finish + for _ in 0..4 { + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + Transition(FOO, TransitionIn), + Update(FOO, TransitionIn), + Render(FOO, TransitionIn), + ] + ); + } + // state finished transitioning in, now moves to active + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(FOO, Active, TransitionIn), + Update(FOO, Active), + Render(FOO, Active), + ] + ); + + states.pop(1)?; + assert_eq!(context.take_log(), vec![]); + + // state begins to transition out to 'dead' + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(FOO, TransitionOut(TransitionTo::Dead), Active), + Transition(FOO, TransitionOut(TransitionTo::Dead)), + Update(FOO, TransitionOut(TransitionTo::Dead)), + Render(FOO, TransitionOut(TransitionTo::Dead)), + ] + ); + // wait for transition to finish + for _ in 0..4 { + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + Transition(FOO, TransitionOut(TransitionTo::Dead)), + Update(FOO, TransitionOut(TransitionTo::Dead)), + Render(FOO, TransitionOut(TransitionTo::Dead)), + ] + ); + } + // state finished transitioning out, now dies + tick(&mut states, &mut context)?; + assert_eq!(context.take_log(), vec![StateChange(FOO, Dead, TransitionOut(TransitionTo::Dead))]); + + // nothing! no states anymore! + tick(&mut states, &mut context)?; + assert_eq!(context.take_log(), vec![]); + + Ok(()) + } + + #[test] + fn push_and_pop_multiple_states() -> Result<(), StateError> { + use LogEntry::*; + use State::*; + + const FIRST: u32 = 1; + const SECOND: u32 = 2; + + let mut states = States::::new(); + let mut context = TestContext::new(); + + // push first state + states.push(TestState::new(FIRST))?; + assert_eq!(context.take_log(), vec![]); + + // first state will transition in + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(FIRST, Pending, Dead), + StateChange(FIRST, TransitionIn, Pending), + Transition(FIRST, TransitionIn), + Update(FIRST, TransitionIn), + Render(FIRST, TransitionIn), + ] + ); + // first state finished transitioning in, now moves to active + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(FIRST, Active, TransitionIn), + Update(FIRST, Active), + Render(FIRST, Active), + ] + ); + + // push second state + states.push(TestState::new(SECOND))?; + assert_eq!(context.take_log(), vec![]); + + // first state begins to transition out to 'paused' state + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(FIRST, TransitionOut(TransitionTo::Paused), Active), + Transition(FIRST, TransitionOut(TransitionTo::Paused)), + Update(FIRST, TransitionOut(TransitionTo::Paused)), + Render(FIRST, TransitionOut(TransitionTo::Paused)), + ] + ); + // state finished transitioning out, now is paused + // second state starts up, will transition in + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(FIRST, Paused, TransitionOut(TransitionTo::Paused)), + StateChange(SECOND, Pending, Dead), + StateChange(SECOND, TransitionIn, Pending), + Transition(SECOND, TransitionIn), + Update(SECOND, TransitionIn), + Render(SECOND, TransitionIn), + ] + ); + // second state finished transitioning in, now moves to active + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(SECOND, Active, TransitionIn), + Update(SECOND, Active), + Render(SECOND, Active), + ] + ); + + // pop second state + states.pop(1)?; + assert_eq!(context.take_log(), vec![]); + + // second state begins to transition out to 'dead' + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(SECOND, TransitionOut(TransitionTo::Dead), Active), + Transition(SECOND, TransitionOut(TransitionTo::Dead)), + Update(SECOND, TransitionOut(TransitionTo::Dead)), + Render(SECOND, TransitionOut(TransitionTo::Dead)), + ] + ); + // second state finished transitioning out, now dies. first state wakes up again and + // starts to transition back in + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(SECOND, Dead, TransitionOut(TransitionTo::Dead)), + StateChange(FIRST, Resume, Paused), + StateChange(FIRST, TransitionIn, Resume), + Transition(FIRST, TransitionIn), + Update(FIRST, TransitionIn), + Render(FIRST, TransitionIn), + ] + ); + // first state finished transitioning in, now moves to active + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(FIRST, Active, TransitionIn), + Update(FIRST, Active), + Render(FIRST, Active), + ] + ); + + // pop first state + states.pop(1)?; + assert_eq!(context.take_log(), vec![]); + + // first state begins to transition out to 'dead' + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(FIRST, TransitionOut(TransitionTo::Dead), Active), + Transition(FIRST, TransitionOut(TransitionTo::Dead)), + Update(FIRST, TransitionOut(TransitionTo::Dead)), + Render(FIRST, TransitionOut(TransitionTo::Dead)), + ] + ); + // first state finished transitioning out, now dies + tick(&mut states, &mut context)?; + assert_eq!(context.take_log(), vec![StateChange(FIRST, Dead, TransitionOut(TransitionTo::Dead))]); + + // nothing! no states anymore! + tick(&mut states, &mut context)?; + assert_eq!(context.take_log(), vec![]); + + Ok(()) + } + + #[test] + fn push_and_pop_multiple_states_with_longer_transitions() -> Result<(), StateError> { + use LogEntry::*; + use State::*; + + const FIRST: u32 = 1; + const SECOND: u32 = 2; + + let mut states = States::::new(); + let mut context = TestContext::new(); + + // push first state + states.push(TestState::new_with_transition_length(FIRST, 3))?; + assert_eq!(context.take_log(), vec![]); + + // first state will transition in + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(FIRST, Pending, Dead), + StateChange(FIRST, TransitionIn, Pending), + Transition(FIRST, TransitionIn), + Update(FIRST, TransitionIn), + Render(FIRST, TransitionIn), + ] + ); + // wait for transition to finish + for _ in 0..2 { + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + Transition(FIRST, TransitionIn), + Update(FIRST, TransitionIn), + Render(FIRST, TransitionIn), + ] + ); + } + // first state finished transitioning in, now moves to active + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(FIRST, Active, TransitionIn), + Update(FIRST, Active), + Render(FIRST, Active), + ] + ); + + // push second state + + states.push(TestState::new_with_transition_length(SECOND, 5))?; + assert_eq!(context.take_log(), vec![]); + + // first state begins to transition out to 'paused' state + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(FIRST, TransitionOut(TransitionTo::Paused), Active), + Transition(FIRST, TransitionOut(TransitionTo::Paused)), + Update(FIRST, TransitionOut(TransitionTo::Paused)), + Render(FIRST, TransitionOut(TransitionTo::Paused)), + ] + ); + // wait for transition to finish + for _ in 0..2 { + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + Transition(FIRST, TransitionOut(TransitionTo::Paused)), + Update(FIRST, TransitionOut(TransitionTo::Paused)), + Render(FIRST, TransitionOut(TransitionTo::Paused)), + ] + ); + } + // first state finished transitioning out, now is paused. second state will transition in + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(FIRST, Paused, TransitionOut(TransitionTo::Paused)), + StateChange(SECOND, Pending, Dead), + StateChange(SECOND, TransitionIn, Pending), + Transition(SECOND, TransitionIn), + Update(SECOND, TransitionIn), + Render(SECOND, TransitionIn), + ] + ); + // wait for transition to finish + for _ in 0..4 { + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + Transition(SECOND, TransitionIn), + Update(SECOND, TransitionIn), + Render(SECOND, TransitionIn), + ] + ); + } + // second state finished transitioning in, now moves to active + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(SECOND, Active, TransitionIn), + Update(SECOND, Active), + Render(SECOND, Active), + ] + ); + + // pop second state + states.pop(1)?; + assert_eq!(context.take_log(), vec![]); + + // second state begins to transition out to 'dead' + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(SECOND, TransitionOut(TransitionTo::Dead), Active), + Transition(SECOND, TransitionOut(TransitionTo::Dead)), + Update(SECOND, TransitionOut(TransitionTo::Dead)), + Render(SECOND, TransitionOut(TransitionTo::Dead)), + ] + ); + // wait for transition to finish + for _ in 0..4 { + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + Transition(SECOND, TransitionOut(TransitionTo::Dead)), + Update(SECOND, TransitionOut(TransitionTo::Dead)), + Render(SECOND, TransitionOut(TransitionTo::Dead)), + ] + ); + } + // second state finished transitioning out, now dies. first state wakes up again and + // starts to transition back in + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(SECOND, Dead, TransitionOut(TransitionTo::Dead)), + StateChange(FIRST, Resume, Paused), + StateChange(FIRST, TransitionIn, Resume), + Transition(FIRST, TransitionIn), + Update(FIRST, TransitionIn), + Render(FIRST, TransitionIn), + ] + ); + // wait for transition to finish + for _ in 0..2 { + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + Transition(FIRST, TransitionIn), + Update(FIRST, TransitionIn), + Render(FIRST, TransitionIn), + ] + ); + } + // first state finished transitioning in, now moves to active + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(FIRST, Active, TransitionIn), + Update(FIRST, Active), + Render(FIRST, Active), + ] + ); + + // pop first state + states.pop(1)?; + assert_eq!(context.take_log(), vec![]); + + // first state begins to transition out to 'dead' + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(FIRST, TransitionOut(TransitionTo::Dead), Active), + Transition(FIRST, TransitionOut(TransitionTo::Dead)), + Update(FIRST, TransitionOut(TransitionTo::Dead)), + Render(FIRST, TransitionOut(TransitionTo::Dead)), + ] + ); + // wait for transition to finish + for _ in 0..2 { + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + Transition(FIRST, TransitionOut(TransitionTo::Dead)), + Update(FIRST, TransitionOut(TransitionTo::Dead)), + Render(FIRST, TransitionOut(TransitionTo::Dead)), + ] + ); + } + // first state finished transitioning out, now dies + tick(&mut states, &mut context)?; + assert_eq!(context.take_log(), vec![StateChange(FIRST, Dead, TransitionOut(TransitionTo::Dead))]); + + // nothing! no states anymore! + tick(&mut states, &mut context)?; + assert_eq!(context.take_log(), vec![]); + + Ok(()) + } + + #[test] + fn pop_multiple_states() -> Result<(), StateError> { + use LogEntry::*; + use State::*; + + const FIRST: u32 = 1; + const SECOND: u32 = 2; + + let mut states = States::::new(); + let mut context = TestContext::new(); + + // push first state + states.push(TestState::new(FIRST))?; + assert_eq!(context.take_log(), vec![]); + + // first state will transition in + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(FIRST, Pending, Dead), + StateChange(FIRST, TransitionIn, Pending), + Transition(FIRST, TransitionIn), + Update(FIRST, TransitionIn), + Render(FIRST, TransitionIn), + ] + ); + // first state finished transitioning in, now moves to active + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(FIRST, Active, TransitionIn), + Update(FIRST, Active), + Render(FIRST, Active), + ] + ); + + // push second state + states.push(TestState::new(SECOND))?; + assert_eq!(context.take_log(), vec![]); + + // first state begins to transition out to 'paused' state + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(FIRST, TransitionOut(TransitionTo::Paused), Active), + Transition(FIRST, TransitionOut(TransitionTo::Paused)), + Update(FIRST, TransitionOut(TransitionTo::Paused)), + Render(FIRST, TransitionOut(TransitionTo::Paused)), + ] + ); + // state finished transitioning out, now is paused + // second state starts up, will transition in + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(FIRST, Paused, TransitionOut(TransitionTo::Paused)), + StateChange(SECOND, Pending, Dead), + StateChange(SECOND, TransitionIn, Pending), + Transition(SECOND, TransitionIn), + Update(SECOND, TransitionIn), + Render(SECOND, TransitionIn), + ] + ); + // second state finished transitioning in, now moves to active + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(SECOND, Active, TransitionIn), + Update(SECOND, Active), + Render(SECOND, Active), + ] + ); + + // pop both states + states.pop(2)?; + assert_eq!(context.take_log(), vec![]); + + // second state begins to transition out to 'dead' + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(SECOND, TransitionOut(TransitionTo::Dead), Active), + Transition(SECOND, TransitionOut(TransitionTo::Dead)), + Update(SECOND, TransitionOut(TransitionTo::Dead)), + Render(SECOND, TransitionOut(TransitionTo::Dead)), + ] + ); + // second state finished transitioning out, now dies. + // first state only goes through a state change, paused to dead. no transition + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(SECOND, Dead, TransitionOut(TransitionTo::Dead)), + StateChange(FIRST, Dead, Paused), + ] + ); + + // nothing! no states anymore! + tick(&mut states, &mut context)?; + assert_eq!(context.take_log(), vec![]); + + Ok(()) + } + + + #[test] + fn swap_states() -> Result<(), StateError> { + use LogEntry::*; + use State::*; + + const FIRST: u32 = 1; + const SECOND: u32 = 2; + + let mut states = States::::new(); + let mut context = TestContext::new(); + + // push first state + states.push(TestState::new(FIRST))?; + assert_eq!(context.take_log(), vec![]); + + // first state will transition in + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(FIRST, Pending, Dead), + StateChange(FIRST, TransitionIn, Pending), + Transition(FIRST, TransitionIn), + Update(FIRST, TransitionIn), + Render(FIRST, TransitionIn), + ] + ); + // first state finished transitioning in, now moves to active + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(FIRST, Active, TransitionIn), + Update(FIRST, Active), + Render(FIRST, Active), + ] + ); + + // swap in second state + states.swap(TestState::new(SECOND))?; + assert_eq!(context.take_log(), vec![]); + + // first state begins to transition out to 'dead' + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(FIRST, TransitionOut(TransitionTo::Dead), Active), + Transition(FIRST, TransitionOut(TransitionTo::Dead)), + Update(FIRST, TransitionOut(TransitionTo::Dead)), + Render(FIRST, TransitionOut(TransitionTo::Dead)), + ] + ); + // first state finished transitioning out, now dies. + // second state starts up, will transition in + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(FIRST, Dead, TransitionOut(TransitionTo::Dead)), + StateChange(SECOND, Pending, Dead), + StateChange(SECOND, TransitionIn, Pending), + Transition(SECOND, TransitionIn), + Update(SECOND, TransitionIn), + Render(SECOND, TransitionIn), + ] + ); + // second state finished transitioning in, now moves to active + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(SECOND, Active, TransitionIn), + Update(SECOND, Active), + Render(SECOND, Active), + ] + ); + + states.pop(1)?; + assert_eq!(context.take_log(), vec![]); + + // state begins to transition out to 'dead' + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(SECOND, TransitionOut(TransitionTo::Dead), Active), + Transition(SECOND, TransitionOut(TransitionTo::Dead)), + Update(SECOND, TransitionOut(TransitionTo::Dead)), + Render(SECOND, TransitionOut(TransitionTo::Dead)), + ] + ); + // state finished transitioning out, now dies + tick(&mut states, &mut context)?; + assert_eq!(context.take_log(), vec![StateChange(SECOND, Dead, TransitionOut(TransitionTo::Dead))]); + + // nothing! no states anymore! + tick(&mut states, &mut context)?; + assert_eq!(context.take_log(), vec![]); + + Ok(()) + } + + struct SelfPushPopState { + id: u32, + counter: u32, + push_after: Option, + pop_after: u32, + } + + impl SelfPushPopState { + pub fn new(id: u32, push_after: Option, pop_after: u32) -> Self { + SelfPushPopState { + id, + counter: 0, + push_after, + pop_after, + } + } + } + + impl AppState for SelfPushPopState { + fn update(&mut self, state: State, context: &mut TestContext) -> Option> { + context.log(LogEntry::Update(self.id, state)); + if state == State::Active { + self.counter += 1; + if self.push_after == Some(self.counter) { + return Some(StateChange::Push(Box::new(SelfPushPopState::new(self.id + 1, None, self.pop_after)))); + } else if self.pop_after == self.counter { + return Some(StateChange::Pop(1)); + } + } + None + } + + fn render(&mut self, state: State, context: &mut TestContext) { + context.log(LogEntry::Render(self.id, state)); + } + + fn transition(&mut self, state: State, context: &mut TestContext) -> bool { + context.log(LogEntry::Transition(self.id, state)); + true + } + + fn state_change(&mut self, new_state: State, old_state: State, context: &mut TestContext) { + context.log(LogEntry::StateChange(self.id, new_state, old_state)); + } + } + + + #[test] + fn state_can_push_and_pop_states_itself() -> Result<(), StateError> { + use LogEntry::*; + use State::*; + + const FIRST: u32 = 1; + const SECOND: u32 = 2; + + let mut states = States::::new(); + let mut context = TestContext::new(); + + // push first state. it will do the rest this time ... + states.push(SelfPushPopState::new(FIRST, Some(5), 10))?; + assert_eq!(context.take_log(), vec![]); + + // first state will transition in + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(FIRST, Pending, Dead), + StateChange(FIRST, TransitionIn, Pending), + Transition(FIRST, TransitionIn), + Update(FIRST, TransitionIn), + Render(FIRST, TransitionIn), + ] + ); + // first state finished transitioning in, now moves to active + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(FIRST, Active, TransitionIn), + Update(FIRST, Active), + Render(FIRST, Active), + ] + ); + // wait for first state's counter to count up to where it should push the second state + for _ in 0..4 { + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + Update(FIRST, Active), + Render(FIRST, Active), + ] + ); + } + + // first state begins to transition out to 'paused' state because it pushed the second state + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(FIRST, TransitionOut(TransitionTo::Paused), Active), + Transition(FIRST, TransitionOut(TransitionTo::Paused)), + Update(FIRST, TransitionOut(TransitionTo::Paused)), + Render(FIRST, TransitionOut(TransitionTo::Paused)), + ] + ); + // first state finished transitioning out, now is paused. second state will transition in + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(FIRST, Paused, TransitionOut(TransitionTo::Paused)), + StateChange(SECOND, Pending, Dead), + StateChange(SECOND, TransitionIn, Pending), + Transition(SECOND, TransitionIn), + Update(SECOND, TransitionIn), + Render(SECOND, TransitionIn), + ] + ); + // second state finished transitioning in, now moves to active + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(SECOND, Active, TransitionIn), + Update(SECOND, Active), + Render(SECOND, Active), + ] + ); + // wait for second state's counter to count up to where it should pop itself + for _ in 0..9 { + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + Update(SECOND, Active), + Render(SECOND, Active), + ] + ); + } + + // second state begins to transition out to 'dead' + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(SECOND, TransitionOut(TransitionTo::Dead), Active), + Transition(SECOND, TransitionOut(TransitionTo::Dead)), + Update(SECOND, TransitionOut(TransitionTo::Dead)), + Render(SECOND, TransitionOut(TransitionTo::Dead)), + ] + ); + // second state finished transitioning out, now dies. first state wakes up again and + // starts to transition back in + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(SECOND, Dead, TransitionOut(TransitionTo::Dead)), + StateChange(FIRST, Resume, Paused), + StateChange(FIRST, TransitionIn, Resume), + Transition(FIRST, TransitionIn), + Update(FIRST, TransitionIn), + Render(FIRST, TransitionIn), + ] + ); + // first state finished transitioning in, now moves to active + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(FIRST, Active, TransitionIn), + Update(FIRST, Active), + Render(FIRST, Active), + ] + ); + // wait for first state's counter to count up to where it should pop itself + for _ in 0..4 { + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + Update(FIRST, Active), + Render(FIRST, Active), + ] + ); + } + + // first state begins to transition out to 'dead' + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(FIRST, TransitionOut(TransitionTo::Dead), Active), + Transition(FIRST, TransitionOut(TransitionTo::Dead)), + Update(FIRST, TransitionOut(TransitionTo::Dead)), + Render(FIRST, TransitionOut(TransitionTo::Dead)), + ] + ); + // first state finished transitioning out, now dies + tick(&mut states, &mut context)?; + assert_eq!(context.take_log(), vec![StateChange(FIRST, Dead, TransitionOut(TransitionTo::Dead))]); + + // nothing! no states anymore! + tick(&mut states, &mut context)?; + assert_eq!(context.take_log(), vec![]); + + Ok(()) + } + + #[test] + fn cannot_push_or_pop_states_when_current_state_not_active() -> Result<(), StateError> { + use LogEntry::*; + use State::*; + + const FOO: u32 = 1; + + let mut states = States::::new(); + let mut context = TestContext::new(); + + states.push(TestState::new(FOO))?; + assert_eq!(context.take_log(), vec![]); + + // state will transition in + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(FOO, Pending, Dead), + StateChange(FOO, TransitionIn, Pending), + Transition(FOO, TransitionIn), + Update(FOO, TransitionIn), + Render(FOO, TransitionIn), + ] + ); + + assert_matches!(states.push(TestState::new(123)), Err(StateError::HasPendingStateChange)); + assert_matches!(states.pop(1), Err(StateError::HasPendingStateChange)); + + // state finished transitioning in, now moves to active + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(FOO, Active, TransitionIn), + Update(FOO, Active), + Render(FOO, Active), + ] + ); + + states.pop(1)?; + assert_eq!(context.take_log(), vec![]); + + // state begins to transition out to 'dead' + tick(&mut states, &mut context)?; + assert_eq!( + context.take_log(), + vec![ + StateChange(FOO, TransitionOut(TransitionTo::Dead), Active), + Transition(FOO, TransitionOut(TransitionTo::Dead)), + Update(FOO, TransitionOut(TransitionTo::Dead)), + Render(FOO, TransitionOut(TransitionTo::Dead)), + ] + ); + + assert_matches!(states.push(TestState::new(123)), Err(StateError::HasPendingStateChange)); + assert_matches!(states.pop(1), Err(StateError::HasPendingStateChange)); + + // state finished transitioning out, now dies + tick(&mut states, &mut context)?; + assert_eq!(context.take_log(), vec![StateChange(FOO, Dead, TransitionOut(TransitionTo::Dead))]); + + states.pop(1)?; + + // nothing! no states anymore! + tick(&mut states, &mut context)?; + assert_eq!(context.take_log(), vec![]); + + Ok(()) + } } diff --git a/libretrogd/src/system/event.rs b/libretrogd/src/system/event.rs index 20aed31..184f0c2 100644 --- a/libretrogd/src/system/event.rs +++ b/libretrogd/src/system/event.rs @@ -16,44 +16,44 @@ use crate::system::MouseEvent::MouseButtonUp; #[derive(Debug, Clone, PartialEq)] pub enum WindowEvent { - Shown, - Hidden, - Exposed, - Moved(i32, i32), - Resized(i32, i32), - SizeChanged(i32, i32), - Minimized, - Maximized, - Restored, - Enter, - Leave, - FocusGained, - FocusLost, - Close, - // for sdl2::event::WindowEvent enum values we haven't mapped / don't care about (yet?) - Unimplemented, + Shown, + Hidden, + Exposed, + Moved(i32, i32), + Resized(i32, i32), + SizeChanged(i32, i32), + Minimized, + Maximized, + Restored, + Enter, + Leave, + FocusGained, + FocusLost, + Close, + // for sdl2::event::WindowEvent enum values we haven't mapped / don't care about (yet?) + Unimplemented, } impl From for WindowEvent { - fn from(value: sdl2::event::WindowEvent) -> Self { - match value { - sdl2::event::WindowEvent::Shown => WindowEvent::Shown, - sdl2::event::WindowEvent::Hidden => WindowEvent::Hidden, - sdl2::event::WindowEvent::Exposed => WindowEvent::Exposed, - sdl2::event::WindowEvent::Moved(x, y) => WindowEvent::Moved(x, y), - sdl2::event::WindowEvent::Resized(width, height) => WindowEvent::Resized(width, height), - sdl2::event::WindowEvent::SizeChanged(width, height) => WindowEvent::SizeChanged(width, height), - sdl2::event::WindowEvent::Minimized => WindowEvent::Minimized, - sdl2::event::WindowEvent::Maximized => WindowEvent::Maximized, - sdl2::event::WindowEvent::Restored => WindowEvent::Restored, - sdl2::event::WindowEvent::Enter => WindowEvent::Enter, - sdl2::event::WindowEvent::Leave => WindowEvent::Leave, - sdl2::event::WindowEvent::FocusGained => WindowEvent::FocusGained, - sdl2::event::WindowEvent::FocusLost => WindowEvent::FocusLost, - sdl2::event::WindowEvent::Close => WindowEvent::Close, - _ => WindowEvent::Unimplemented, - } - } + fn from(value: sdl2::event::WindowEvent) -> Self { + match value { + sdl2::event::WindowEvent::Shown => WindowEvent::Shown, + sdl2::event::WindowEvent::Hidden => WindowEvent::Hidden, + sdl2::event::WindowEvent::Exposed => WindowEvent::Exposed, + sdl2::event::WindowEvent::Moved(x, y) => WindowEvent::Moved(x, y), + sdl2::event::WindowEvent::Resized(width, height) => WindowEvent::Resized(width, height), + sdl2::event::WindowEvent::SizeChanged(width, height) => WindowEvent::SizeChanged(width, height), + sdl2::event::WindowEvent::Minimized => WindowEvent::Minimized, + sdl2::event::WindowEvent::Maximized => WindowEvent::Maximized, + sdl2::event::WindowEvent::Restored => WindowEvent::Restored, + sdl2::event::WindowEvent::Enter => WindowEvent::Enter, + sdl2::event::WindowEvent::Leave => WindowEvent::Leave, + sdl2::event::WindowEvent::FocusGained => WindowEvent::FocusGained, + sdl2::event::WindowEvent::FocusLost => WindowEvent::FocusLost, + sdl2::event::WindowEvent::Close => WindowEvent::Close, + _ => WindowEvent::Unimplemented, + } + } } bitflags! { @@ -76,156 +76,156 @@ bitflags! { #[derive(Debug, Clone, PartialEq)] pub enum KeyboardEvent { - KeyUp { - keycode: Option, - scancode: Option, - keymod: KeyModifiers, - repeat: bool, - }, - KeyDown { - keycode: Option, - scancode: Option, - keymod: KeyModifiers, - repeat: bool, - }, + KeyUp { + keycode: Option, + scancode: Option, + keymod: KeyModifiers, + repeat: bool, + }, + KeyDown { + keycode: Option, + scancode: Option, + keymod: KeyModifiers, + repeat: bool, + }, } #[derive(Debug, Clone, PartialEq)] pub enum MouseEvent { - MouseMotion { - x: i32, - y: i32, - x_delta: i32, - y_delta: i32, - buttons: MouseButtons, - }, - MouseButtonDown { - x: i32, - y: i32, - button: MouseButton, - clicks: u8, - }, - MouseButtonUp { - x: i32, - y: i32, - button: MouseButton, - clicks: u8, - }, + MouseMotion { + x: i32, + y: i32, + x_delta: i32, + y_delta: i32, + buttons: MouseButtons, + }, + MouseButtonDown { + x: i32, + y: i32, + button: MouseButton, + clicks: u8, + }, + MouseButtonUp { + x: i32, + y: i32, + button: MouseButton, + clicks: u8, + }, } #[derive(Debug, Clone, PartialEq)] pub enum SystemEvent { - Quit, - AppTerminating, - AppLowMemory, - AppWillEnterBackground, - AppDidEnterBackground, - AppWillEnterForeground, - AppDidEnterForeground, - Window(WindowEvent), - Keyboard(KeyboardEvent), - Mouse(MouseEvent), - // for the many sdl2::event::Event enum values that we don't are about quite yet ... - Unimplemented, + Quit, + AppTerminating, + AppLowMemory, + AppWillEnterBackground, + AppDidEnterBackground, + AppWillEnterForeground, + AppDidEnterForeground, + Window(WindowEvent), + Keyboard(KeyboardEvent), + Mouse(MouseEvent), + // for the many sdl2::event::Event enum values that we don't are about quite yet ... + Unimplemented, } impl From for SystemEvent { - fn from(value: sdl2::event::Event) -> Self { - match value { - sdl2::event::Event::Quit { .. } => SystemEvent::Quit, - sdl2::event::Event::AppTerminating { .. } => SystemEvent::AppTerminating, - sdl2::event::Event::AppLowMemory { .. } => SystemEvent::AppLowMemory, - sdl2::event::Event::AppWillEnterBackground { .. } => SystemEvent::AppWillEnterBackground, - sdl2::event::Event::AppDidEnterBackground { .. } => SystemEvent::AppDidEnterBackground, - sdl2::event::Event::AppWillEnterForeground { .. } => SystemEvent::AppWillEnterForeground, - sdl2::event::Event::AppDidEnterForeground { .. } => SystemEvent::AppDidEnterForeground, - sdl2::event::Event::Window { win_event, .. } => SystemEvent::Window(win_event.into()), + fn from(value: sdl2::event::Event) -> Self { + match value { + sdl2::event::Event::Quit { .. } => SystemEvent::Quit, + sdl2::event::Event::AppTerminating { .. } => SystemEvent::AppTerminating, + sdl2::event::Event::AppLowMemory { .. } => SystemEvent::AppLowMemory, + sdl2::event::Event::AppWillEnterBackground { .. } => SystemEvent::AppWillEnterBackground, + sdl2::event::Event::AppDidEnterBackground { .. } => SystemEvent::AppDidEnterBackground, + sdl2::event::Event::AppWillEnterForeground { .. } => SystemEvent::AppWillEnterForeground, + sdl2::event::Event::AppDidEnterForeground { .. } => SystemEvent::AppDidEnterForeground, + sdl2::event::Event::Window { win_event, .. } => SystemEvent::Window(win_event.into()), - sdl2::event::Event::KeyDown { keycode, scancode, keymod, repeat, .. } => { - SystemEvent::Keyboard(KeyboardEvent::KeyDown { - keycode: keycode.map(|keycode| keycode.into()), - scancode: scancode.map(|scancode| scancode.into()), - keymod: KeyModifiers::from_bits_truncate(keymod.bits()), - repeat - }) - }, - sdl2::event::Event::KeyUp { keycode, scancode, keymod, repeat, .. } => { - SystemEvent::Keyboard(KeyboardEvent::KeyUp { - keycode: keycode.map(|keycode| keycode.into()), - scancode: scancode.map(|scancode| scancode.into()), - keymod: KeyModifiers::from_bits_truncate(keymod.bits()), - repeat - }) - } - sdl2::event::Event::MouseMotion { mousestate, x, y, xrel, yrel, .. } => { - SystemEvent::Mouse(MouseEvent::MouseMotion { - x, - y, - x_delta: xrel, - y_delta: yrel, - buttons: MouseButtons::from_bits_truncate(mousestate.to_sdl_state()), - }) - }, - sdl2::event::Event::MouseButtonDown { mouse_btn, clicks, x, y, .. } => { - SystemEvent::Mouse(MouseEvent::MouseButtonDown { - x, - y, - clicks, - button: mouse_btn.into(), - }) - }, - sdl2::event::Event::MouseButtonUp { mouse_btn, clicks, x, y, .. } => { - SystemEvent::Mouse(MouseButtonUp { - x, - y, - clicks, - button: mouse_btn.into(), - }) - }, + sdl2::event::Event::KeyDown { keycode, scancode, keymod, repeat, .. } => { + SystemEvent::Keyboard(KeyboardEvent::KeyDown { + keycode: keycode.map(|keycode| keycode.into()), + scancode: scancode.map(|scancode| scancode.into()), + keymod: KeyModifiers::from_bits_truncate(keymod.bits()), + repeat, + }) + } + sdl2::event::Event::KeyUp { keycode, scancode, keymod, repeat, .. } => { + SystemEvent::Keyboard(KeyboardEvent::KeyUp { + keycode: keycode.map(|keycode| keycode.into()), + scancode: scancode.map(|scancode| scancode.into()), + keymod: KeyModifiers::from_bits_truncate(keymod.bits()), + repeat, + }) + } + sdl2::event::Event::MouseMotion { mousestate, x, y, xrel, yrel, .. } => { + SystemEvent::Mouse(MouseEvent::MouseMotion { + x, + y, + x_delta: xrel, + y_delta: yrel, + buttons: MouseButtons::from_bits_truncate(mousestate.to_sdl_state()), + }) + } + sdl2::event::Event::MouseButtonDown { mouse_btn, clicks, x, y, .. } => { + SystemEvent::Mouse(MouseEvent::MouseButtonDown { + x, + y, + clicks, + button: mouse_btn.into(), + }) + } + sdl2::event::Event::MouseButtonUp { mouse_btn, clicks, x, y, .. } => { + SystemEvent::Mouse(MouseButtonUp { + x, + y, + clicks, + button: mouse_btn.into(), + }) + } - _ => SystemEvent::Unimplemented, - } - } + _ => SystemEvent::Unimplemented, + } + } } /// Common trait for implementing a handler of [`SystemEvent`]s that are polled during the /// application's main loop. pub trait SystemEventHandler { - /// Processes the data from the given [`SystemEvent`]. Returns true if the processing actually - /// recognized the passed event and handled it, or false if the event was ignored. - fn handle_event(&mut self, event: &SystemEvent) -> bool; + /// Processes the data from the given [`SystemEvent`]. Returns true if the processing actually + /// recognized the passed event and handled it, or false if the event was ignored. + fn handle_event(&mut self, event: &SystemEvent) -> bool; } /// An interator for SDL2 system events, polled via [`SystemEventPump`]. pub struct SystemEventIterator<'a> { - iter: sdl2::event::EventPollIterator<'a>, + iter: sdl2::event::EventPollIterator<'a>, } impl Iterator for SystemEventIterator<'_> { - type Item = SystemEvent; + type Item = SystemEvent; - fn next(&mut self) -> Option { - self.iter.next().map(|e| e.into()) - } + fn next(&mut self) -> Option { + self.iter.next().map(|e| e.into()) + } } /// Provides an event pump iterator that wraps over SDL2 events, allowing applications to respond /// to all events each frame as [`SystemEvent`] instances. pub struct SystemEventPump { - sdl_event_pump: sdl2::EventPump, + sdl_event_pump: sdl2::EventPump, } impl SystemEventPump { - pub fn from(pump: sdl2::EventPump) -> Self { - SystemEventPump { - sdl_event_pump: pump, - } - } + pub fn from(pump: sdl2::EventPump) -> Self { + SystemEventPump { + sdl_event_pump: pump, + } + } - /// Returns an iterator over [`SystemEvent`]s that have been generated since the last time - /// events were polled (usually, in the previous frame). - pub fn poll_iter(&mut self) -> SystemEventIterator { - self.sdl_event_pump.pump_events(); - SystemEventIterator { iter: self.sdl_event_pump.poll_iter() } - } + /// Returns an iterator over [`SystemEvent`]s that have been generated since the last time + /// events were polled (usually, in the previous frame). + pub fn poll_iter(&mut self) -> SystemEventIterator { + self.sdl_event_pump.pump_events(); + SystemEventIterator { iter: self.sdl_event_pump.poll_iter() } + } } diff --git a/libretrogd/src/system/input_devices/keyboard/codes.rs b/libretrogd/src/system/input_devices/keyboard/codes.rs index 1f4b434..d3787c3 100644 --- a/libretrogd/src/system/input_devices/keyboard/codes.rs +++ b/libretrogd/src/system/input_devices/keyboard/codes.rs @@ -3,481 +3,481 @@ #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] #[repr(i32)] pub enum Keycode { - Backspace = sdl2::keyboard::Keycode::Backspace as i32, - Tab = sdl2::keyboard::Keycode::Tab as i32, - Return = sdl2::keyboard::Keycode::Return as i32, - Escape = sdl2::keyboard::Keycode::Escape as i32, - Space = sdl2::keyboard::Keycode::Space as i32, - Exclaim = sdl2::keyboard::Keycode::Exclaim as i32, - Quotedbl = sdl2::keyboard::Keycode::Quotedbl as i32, - Hash = sdl2::keyboard::Keycode::Hash as i32, - Dollar = sdl2::keyboard::Keycode::Dollar as i32, - Percent = sdl2::keyboard::Keycode::Percent as i32, - Ampersand = sdl2::keyboard::Keycode::Ampersand as i32, - Quote = sdl2::keyboard::Keycode::Quote as i32, - LeftParen = sdl2::keyboard::Keycode::LeftParen as i32, - RightParen = sdl2::keyboard::Keycode::RightParen as i32, - Asterisk = sdl2::keyboard::Keycode::Asterisk as i32, - Plus = sdl2::keyboard::Keycode::Plus as i32, - Comma = sdl2::keyboard::Keycode::Comma as i32, - Minus = sdl2::keyboard::Keycode::Minus as i32, - Period = sdl2::keyboard::Keycode::Period as i32, - Slash = sdl2::keyboard::Keycode::Slash as i32, - Num0 = sdl2::keyboard::Keycode::Num0 as i32, - Num1 = sdl2::keyboard::Keycode::Num1 as i32, - Num2 = sdl2::keyboard::Keycode::Num2 as i32, - Num3 = sdl2::keyboard::Keycode::Num3 as i32, - Num4 = sdl2::keyboard::Keycode::Num4 as i32, - Num5 = sdl2::keyboard::Keycode::Num5 as i32, - Num6 = sdl2::keyboard::Keycode::Num6 as i32, - Num7 = sdl2::keyboard::Keycode::Num7 as i32, - Num8 = sdl2::keyboard::Keycode::Num8 as i32, - Num9 = sdl2::keyboard::Keycode::Num9 as i32, - Colon = sdl2::keyboard::Keycode::Colon as i32, - Semicolon = sdl2::keyboard::Keycode::Semicolon as i32, - Less = sdl2::keyboard::Keycode::Less as i32, - Equals = sdl2::keyboard::Keycode::Equals as i32, - Greater = sdl2::keyboard::Keycode::Greater as i32, - Question = sdl2::keyboard::Keycode::Question as i32, - At = sdl2::keyboard::Keycode::At as i32, - LeftBracket = sdl2::keyboard::Keycode::LeftBracket as i32, - Backslash = sdl2::keyboard::Keycode::Backslash as i32, - RightBracket = sdl2::keyboard::Keycode::RightBracket as i32, - Caret = sdl2::keyboard::Keycode::Caret as i32, - Underscore = sdl2::keyboard::Keycode::Underscore as i32, - Backquote = sdl2::keyboard::Keycode::Backquote as i32, - A = sdl2::keyboard::Keycode::A as i32, - B = sdl2::keyboard::Keycode::B as i32, - C = sdl2::keyboard::Keycode::C as i32, - D = sdl2::keyboard::Keycode::D as i32, - E = sdl2::keyboard::Keycode::E as i32, - F = sdl2::keyboard::Keycode::F as i32, - G = sdl2::keyboard::Keycode::G as i32, - H = sdl2::keyboard::Keycode::H as i32, - I = sdl2::keyboard::Keycode::I as i32, - J = sdl2::keyboard::Keycode::J as i32, - K = sdl2::keyboard::Keycode::K as i32, - L = sdl2::keyboard::Keycode::L as i32, - M = sdl2::keyboard::Keycode::M as i32, - N = sdl2::keyboard::Keycode::N as i32, - O = sdl2::keyboard::Keycode::O as i32, - P = sdl2::keyboard::Keycode::P as i32, - Q = sdl2::keyboard::Keycode::Q as i32, - R = sdl2::keyboard::Keycode::R as i32, - S = sdl2::keyboard::Keycode::S as i32, - T = sdl2::keyboard::Keycode::T as i32, - U = sdl2::keyboard::Keycode::U as i32, - V = sdl2::keyboard::Keycode::V as i32, - W = sdl2::keyboard::Keycode::W as i32, - X = sdl2::keyboard::Keycode::X as i32, - Y = sdl2::keyboard::Keycode::Y as i32, - Z = sdl2::keyboard::Keycode::Z as i32, - Delete = sdl2::keyboard::Keycode::Delete as i32, - CapsLock = sdl2::keyboard::Keycode::CapsLock as i32, - F1 = sdl2::keyboard::Keycode::F1 as i32, - F2 = sdl2::keyboard::Keycode::F2 as i32, - F3 = sdl2::keyboard::Keycode::F3 as i32, - F4 = sdl2::keyboard::Keycode::F4 as i32, - F5 = sdl2::keyboard::Keycode::F5 as i32, - F6 = sdl2::keyboard::Keycode::F6 as i32, - F7 = sdl2::keyboard::Keycode::F7 as i32, - F8 = sdl2::keyboard::Keycode::F8 as i32, - F9 = sdl2::keyboard::Keycode::F9 as i32, - F10 = sdl2::keyboard::Keycode::F10 as i32, - F11 = sdl2::keyboard::Keycode::F11 as i32, - F12 = sdl2::keyboard::Keycode::F12 as i32, - PrintScreen = sdl2::keyboard::Keycode::PrintScreen as i32, - ScrollLock = sdl2::keyboard::Keycode::ScrollLock as i32, - Pause = sdl2::keyboard::Keycode::Pause as i32, - Insert = sdl2::keyboard::Keycode::Insert as i32, - Home = sdl2::keyboard::Keycode::Home as i32, - PageUp = sdl2::keyboard::Keycode::PageUp as i32, - End = sdl2::keyboard::Keycode::End as i32, - PageDown = sdl2::keyboard::Keycode::PageDown as i32, - Right = sdl2::keyboard::Keycode::Right as i32, - Left = sdl2::keyboard::Keycode::Left as i32, - Down = sdl2::keyboard::Keycode::Down as i32, - Up = sdl2::keyboard::Keycode::Up as i32, - NumLockClear = sdl2::keyboard::Keycode::NumLockClear as i32, - KpDivide = sdl2::keyboard::Keycode::KpDivide as i32, - KpMultiply = sdl2::keyboard::Keycode::KpMultiply as i32, - KpMinus = sdl2::keyboard::Keycode::KpMinus as i32, - KpPlus = sdl2::keyboard::Keycode::KpPlus as i32, - KpEnter = sdl2::keyboard::Keycode::KpEnter as i32, - Kp1 = sdl2::keyboard::Keycode::Kp1 as i32, - Kp2 = sdl2::keyboard::Keycode::Kp2 as i32, - Kp3 = sdl2::keyboard::Keycode::Kp3 as i32, - Kp4 = sdl2::keyboard::Keycode::Kp4 as i32, - Kp5 = sdl2::keyboard::Keycode::Kp5 as i32, - Kp6 = sdl2::keyboard::Keycode::Kp6 as i32, - Kp7 = sdl2::keyboard::Keycode::Kp7 as i32, - Kp8 = sdl2::keyboard::Keycode::Kp8 as i32, - Kp9 = sdl2::keyboard::Keycode::Kp9 as i32, - Kp0 = sdl2::keyboard::Keycode::Kp0 as i32, - KpPeriod = sdl2::keyboard::Keycode::KpPeriod as i32, - Application = sdl2::keyboard::Keycode::Application as i32, - Power = sdl2::keyboard::Keycode::Power as i32, - KpEquals = sdl2::keyboard::Keycode::KpEquals as i32, - F13 = sdl2::keyboard::Keycode::F13 as i32, - F14 = sdl2::keyboard::Keycode::F14 as i32, - F15 = sdl2::keyboard::Keycode::F15 as i32, - F16 = sdl2::keyboard::Keycode::F16 as i32, - F17 = sdl2::keyboard::Keycode::F17 as i32, - F18 = sdl2::keyboard::Keycode::F18 as i32, - F19 = sdl2::keyboard::Keycode::F19 as i32, - F20 = sdl2::keyboard::Keycode::F20 as i32, - F21 = sdl2::keyboard::Keycode::F21 as i32, - F22 = sdl2::keyboard::Keycode::F22 as i32, - F23 = sdl2::keyboard::Keycode::F23 as i32, - F24 = sdl2::keyboard::Keycode::F24 as i32, - Execute = sdl2::keyboard::Keycode::Execute as i32, - Help = sdl2::keyboard::Keycode::Help as i32, - Menu = sdl2::keyboard::Keycode::Menu as i32, - Select = sdl2::keyboard::Keycode::Select as i32, - Stop = sdl2::keyboard::Keycode::Stop as i32, - Again = sdl2::keyboard::Keycode::Again as i32, - Undo = sdl2::keyboard::Keycode::Undo as i32, - Cut = sdl2::keyboard::Keycode::Cut as i32, - Copy = sdl2::keyboard::Keycode::Copy as i32, - Paste = sdl2::keyboard::Keycode::Paste as i32, - Find = sdl2::keyboard::Keycode::Find as i32, - Mute = sdl2::keyboard::Keycode::Mute as i32, - VolumeUp = sdl2::keyboard::Keycode::VolumeUp as i32, - VolumeDown = sdl2::keyboard::Keycode::VolumeDown as i32, - KpComma = sdl2::keyboard::Keycode::KpComma as i32, - KpEqualsAS400 = sdl2::keyboard::Keycode::KpEqualsAS400 as i32, - AltErase = sdl2::keyboard::Keycode::AltErase as i32, - Sysreq = sdl2::keyboard::Keycode::Sysreq as i32, - Cancel = sdl2::keyboard::Keycode::Cancel as i32, - Clear = sdl2::keyboard::Keycode::Clear as i32, - Prior = sdl2::keyboard::Keycode::Prior as i32, - Return2 = sdl2::keyboard::Keycode::Return2 as i32, - Separator = sdl2::keyboard::Keycode::Separator as i32, - Out = sdl2::keyboard::Keycode::Out as i32, - Oper = sdl2::keyboard::Keycode::Oper as i32, - ClearAgain = sdl2::keyboard::Keycode::ClearAgain as i32, - CrSel = sdl2::keyboard::Keycode::CrSel as i32, - ExSel = sdl2::keyboard::Keycode::ExSel as i32, - Kp00 = sdl2::keyboard::Keycode::Kp00 as i32, - Kp000 = sdl2::keyboard::Keycode::Kp000 as i32, - ThousandsSeparator = sdl2::keyboard::Keycode::ThousandsSeparator as i32, - DecimalSeparator = sdl2::keyboard::Keycode::DecimalSeparator as i32, - CurrencyUnit = sdl2::keyboard::Keycode::CurrencyUnit as i32, - CurrencySubUnit = sdl2::keyboard::Keycode::CurrencySubUnit as i32, - KpLeftParen = sdl2::keyboard::Keycode::KpLeftParen as i32, - KpRightParen = sdl2::keyboard::Keycode::KpRightParen as i32, - KpLeftBrace = sdl2::keyboard::Keycode::KpLeftBrace as i32, - KpRightBrace = sdl2::keyboard::Keycode::KpRightBrace as i32, - KpTab = sdl2::keyboard::Keycode::KpTab as i32, - KpBackspace = sdl2::keyboard::Keycode::KpBackspace as i32, - KpA = sdl2::keyboard::Keycode::KpA as i32, - KpB = sdl2::keyboard::Keycode::KpB as i32, - KpC = sdl2::keyboard::Keycode::KpC as i32, - KpD = sdl2::keyboard::Keycode::KpD as i32, - KpE = sdl2::keyboard::Keycode::KpE as i32, - KpF = sdl2::keyboard::Keycode::KpF as i32, - KpXor = sdl2::keyboard::Keycode::KpXor as i32, - KpPower = sdl2::keyboard::Keycode::KpPower as i32, - KpPercent = sdl2::keyboard::Keycode::KpPercent as i32, - KpLess = sdl2::keyboard::Keycode::KpLess as i32, - KpGreater = sdl2::keyboard::Keycode::KpGreater as i32, - KpAmpersand = sdl2::keyboard::Keycode::KpAmpersand as i32, - KpDblAmpersand = sdl2::keyboard::Keycode::KpDblAmpersand as i32, - KpVerticalBar = sdl2::keyboard::Keycode::KpVerticalBar as i32, - KpDblVerticalBar = sdl2::keyboard::Keycode::KpDblVerticalBar as i32, - KpColon = sdl2::keyboard::Keycode::KpColon as i32, - KpHash = sdl2::keyboard::Keycode::KpHash as i32, - KpSpace = sdl2::keyboard::Keycode::KpSpace as i32, - KpAt = sdl2::keyboard::Keycode::KpAt as i32, - KpExclam = sdl2::keyboard::Keycode::KpExclam as i32, - KpMemStore = sdl2::keyboard::Keycode::KpMemStore as i32, - KpMemRecall = sdl2::keyboard::Keycode::KpMemRecall as i32, - KpMemClear = sdl2::keyboard::Keycode::KpMemClear as i32, - KpMemAdd = sdl2::keyboard::Keycode::KpMemAdd as i32, - KpMemSubtract = sdl2::keyboard::Keycode::KpMemSubtract as i32, - KpMemMultiply = sdl2::keyboard::Keycode::KpMemMultiply as i32, - KpMemDivide = sdl2::keyboard::Keycode::KpMemDivide as i32, - KpPlusMinus = sdl2::keyboard::Keycode::KpPlusMinus as i32, - KpClear = sdl2::keyboard::Keycode::KpClear as i32, - KpClearEntry = sdl2::keyboard::Keycode::KpClearEntry as i32, - KpBinary = sdl2::keyboard::Keycode::KpBinary as i32, - KpOctal = sdl2::keyboard::Keycode::KpOctal as i32, - KpDecimal = sdl2::keyboard::Keycode::KpDecimal as i32, - KpHexadecimal = sdl2::keyboard::Keycode::KpHexadecimal as i32, - LCtrl = sdl2::keyboard::Keycode::LCtrl as i32, - LShift = sdl2::keyboard::Keycode::LShift as i32, - LAlt = sdl2::keyboard::Keycode::LAlt as i32, - LGui = sdl2::keyboard::Keycode::LGui as i32, - RCtrl = sdl2::keyboard::Keycode::RCtrl as i32, - RShift = sdl2::keyboard::Keycode::RShift as i32, - RAlt = sdl2::keyboard::Keycode::RAlt as i32, - RGui = sdl2::keyboard::Keycode::RGui as i32, - Mode = sdl2::keyboard::Keycode::Mode as i32, - AudioNext = sdl2::keyboard::Keycode::AudioNext as i32, - AudioPrev = sdl2::keyboard::Keycode::AudioPrev as i32, - AudioStop = sdl2::keyboard::Keycode::AudioStop as i32, - AudioPlay = sdl2::keyboard::Keycode::AudioPlay as i32, - AudioMute = sdl2::keyboard::Keycode::AudioMute as i32, - MediaSelect = sdl2::keyboard::Keycode::MediaSelect as i32, - Www = sdl2::keyboard::Keycode::Www as i32, - Mail = sdl2::keyboard::Keycode::Mail as i32, - Calculator = sdl2::keyboard::Keycode::Calculator as i32, - Computer = sdl2::keyboard::Keycode::Computer as i32, - AcSearch = sdl2::keyboard::Keycode::AcSearch as i32, - AcHome = sdl2::keyboard::Keycode::AcHome as i32, - AcBack = sdl2::keyboard::Keycode::AcBack as i32, - AcForward = sdl2::keyboard::Keycode::AcForward as i32, - AcStop = sdl2::keyboard::Keycode::AcStop as i32, - AcRefresh = sdl2::keyboard::Keycode::AcRefresh as i32, - AcBookmarks = sdl2::keyboard::Keycode::AcBookmarks as i32, - BrightnessDown = sdl2::keyboard::Keycode::BrightnessDown as i32, - BrightnessUp = sdl2::keyboard::Keycode::BrightnessUp as i32, - DisplaySwitch = sdl2::keyboard::Keycode::DisplaySwitch as i32, - KbdIllumToggle = sdl2::keyboard::Keycode::KbdIllumToggle as i32, - KbdIllumDown = sdl2::keyboard::Keycode::KbdIllumDown as i32, - KbdIllumUp = sdl2::keyboard::Keycode::KbdIllumUp as i32, - Eject = sdl2::keyboard::Keycode::Eject as i32, - Sleep = sdl2::keyboard::Keycode::Sleep as i32, + Backspace = sdl2::keyboard::Keycode::Backspace as i32, + Tab = sdl2::keyboard::Keycode::Tab as i32, + Return = sdl2::keyboard::Keycode::Return as i32, + Escape = sdl2::keyboard::Keycode::Escape as i32, + Space = sdl2::keyboard::Keycode::Space as i32, + Exclaim = sdl2::keyboard::Keycode::Exclaim as i32, + Quotedbl = sdl2::keyboard::Keycode::Quotedbl as i32, + Hash = sdl2::keyboard::Keycode::Hash as i32, + Dollar = sdl2::keyboard::Keycode::Dollar as i32, + Percent = sdl2::keyboard::Keycode::Percent as i32, + Ampersand = sdl2::keyboard::Keycode::Ampersand as i32, + Quote = sdl2::keyboard::Keycode::Quote as i32, + LeftParen = sdl2::keyboard::Keycode::LeftParen as i32, + RightParen = sdl2::keyboard::Keycode::RightParen as i32, + Asterisk = sdl2::keyboard::Keycode::Asterisk as i32, + Plus = sdl2::keyboard::Keycode::Plus as i32, + Comma = sdl2::keyboard::Keycode::Comma as i32, + Minus = sdl2::keyboard::Keycode::Minus as i32, + Period = sdl2::keyboard::Keycode::Period as i32, + Slash = sdl2::keyboard::Keycode::Slash as i32, + Num0 = sdl2::keyboard::Keycode::Num0 as i32, + Num1 = sdl2::keyboard::Keycode::Num1 as i32, + Num2 = sdl2::keyboard::Keycode::Num2 as i32, + Num3 = sdl2::keyboard::Keycode::Num3 as i32, + Num4 = sdl2::keyboard::Keycode::Num4 as i32, + Num5 = sdl2::keyboard::Keycode::Num5 as i32, + Num6 = sdl2::keyboard::Keycode::Num6 as i32, + Num7 = sdl2::keyboard::Keycode::Num7 as i32, + Num8 = sdl2::keyboard::Keycode::Num8 as i32, + Num9 = sdl2::keyboard::Keycode::Num9 as i32, + Colon = sdl2::keyboard::Keycode::Colon as i32, + Semicolon = sdl2::keyboard::Keycode::Semicolon as i32, + Less = sdl2::keyboard::Keycode::Less as i32, + Equals = sdl2::keyboard::Keycode::Equals as i32, + Greater = sdl2::keyboard::Keycode::Greater as i32, + Question = sdl2::keyboard::Keycode::Question as i32, + At = sdl2::keyboard::Keycode::At as i32, + LeftBracket = sdl2::keyboard::Keycode::LeftBracket as i32, + Backslash = sdl2::keyboard::Keycode::Backslash as i32, + RightBracket = sdl2::keyboard::Keycode::RightBracket as i32, + Caret = sdl2::keyboard::Keycode::Caret as i32, + Underscore = sdl2::keyboard::Keycode::Underscore as i32, + Backquote = sdl2::keyboard::Keycode::Backquote as i32, + A = sdl2::keyboard::Keycode::A as i32, + B = sdl2::keyboard::Keycode::B as i32, + C = sdl2::keyboard::Keycode::C as i32, + D = sdl2::keyboard::Keycode::D as i32, + E = sdl2::keyboard::Keycode::E as i32, + F = sdl2::keyboard::Keycode::F as i32, + G = sdl2::keyboard::Keycode::G as i32, + H = sdl2::keyboard::Keycode::H as i32, + I = sdl2::keyboard::Keycode::I as i32, + J = sdl2::keyboard::Keycode::J as i32, + K = sdl2::keyboard::Keycode::K as i32, + L = sdl2::keyboard::Keycode::L as i32, + M = sdl2::keyboard::Keycode::M as i32, + N = sdl2::keyboard::Keycode::N as i32, + O = sdl2::keyboard::Keycode::O as i32, + P = sdl2::keyboard::Keycode::P as i32, + Q = sdl2::keyboard::Keycode::Q as i32, + R = sdl2::keyboard::Keycode::R as i32, + S = sdl2::keyboard::Keycode::S as i32, + T = sdl2::keyboard::Keycode::T as i32, + U = sdl2::keyboard::Keycode::U as i32, + V = sdl2::keyboard::Keycode::V as i32, + W = sdl2::keyboard::Keycode::W as i32, + X = sdl2::keyboard::Keycode::X as i32, + Y = sdl2::keyboard::Keycode::Y as i32, + Z = sdl2::keyboard::Keycode::Z as i32, + Delete = sdl2::keyboard::Keycode::Delete as i32, + CapsLock = sdl2::keyboard::Keycode::CapsLock as i32, + F1 = sdl2::keyboard::Keycode::F1 as i32, + F2 = sdl2::keyboard::Keycode::F2 as i32, + F3 = sdl2::keyboard::Keycode::F3 as i32, + F4 = sdl2::keyboard::Keycode::F4 as i32, + F5 = sdl2::keyboard::Keycode::F5 as i32, + F6 = sdl2::keyboard::Keycode::F6 as i32, + F7 = sdl2::keyboard::Keycode::F7 as i32, + F8 = sdl2::keyboard::Keycode::F8 as i32, + F9 = sdl2::keyboard::Keycode::F9 as i32, + F10 = sdl2::keyboard::Keycode::F10 as i32, + F11 = sdl2::keyboard::Keycode::F11 as i32, + F12 = sdl2::keyboard::Keycode::F12 as i32, + PrintScreen = sdl2::keyboard::Keycode::PrintScreen as i32, + ScrollLock = sdl2::keyboard::Keycode::ScrollLock as i32, + Pause = sdl2::keyboard::Keycode::Pause as i32, + Insert = sdl2::keyboard::Keycode::Insert as i32, + Home = sdl2::keyboard::Keycode::Home as i32, + PageUp = sdl2::keyboard::Keycode::PageUp as i32, + End = sdl2::keyboard::Keycode::End as i32, + PageDown = sdl2::keyboard::Keycode::PageDown as i32, + Right = sdl2::keyboard::Keycode::Right as i32, + Left = sdl2::keyboard::Keycode::Left as i32, + Down = sdl2::keyboard::Keycode::Down as i32, + Up = sdl2::keyboard::Keycode::Up as i32, + NumLockClear = sdl2::keyboard::Keycode::NumLockClear as i32, + KpDivide = sdl2::keyboard::Keycode::KpDivide as i32, + KpMultiply = sdl2::keyboard::Keycode::KpMultiply as i32, + KpMinus = sdl2::keyboard::Keycode::KpMinus as i32, + KpPlus = sdl2::keyboard::Keycode::KpPlus as i32, + KpEnter = sdl2::keyboard::Keycode::KpEnter as i32, + Kp1 = sdl2::keyboard::Keycode::Kp1 as i32, + Kp2 = sdl2::keyboard::Keycode::Kp2 as i32, + Kp3 = sdl2::keyboard::Keycode::Kp3 as i32, + Kp4 = sdl2::keyboard::Keycode::Kp4 as i32, + Kp5 = sdl2::keyboard::Keycode::Kp5 as i32, + Kp6 = sdl2::keyboard::Keycode::Kp6 as i32, + Kp7 = sdl2::keyboard::Keycode::Kp7 as i32, + Kp8 = sdl2::keyboard::Keycode::Kp8 as i32, + Kp9 = sdl2::keyboard::Keycode::Kp9 as i32, + Kp0 = sdl2::keyboard::Keycode::Kp0 as i32, + KpPeriod = sdl2::keyboard::Keycode::KpPeriod as i32, + Application = sdl2::keyboard::Keycode::Application as i32, + Power = sdl2::keyboard::Keycode::Power as i32, + KpEquals = sdl2::keyboard::Keycode::KpEquals as i32, + F13 = sdl2::keyboard::Keycode::F13 as i32, + F14 = sdl2::keyboard::Keycode::F14 as i32, + F15 = sdl2::keyboard::Keycode::F15 as i32, + F16 = sdl2::keyboard::Keycode::F16 as i32, + F17 = sdl2::keyboard::Keycode::F17 as i32, + F18 = sdl2::keyboard::Keycode::F18 as i32, + F19 = sdl2::keyboard::Keycode::F19 as i32, + F20 = sdl2::keyboard::Keycode::F20 as i32, + F21 = sdl2::keyboard::Keycode::F21 as i32, + F22 = sdl2::keyboard::Keycode::F22 as i32, + F23 = sdl2::keyboard::Keycode::F23 as i32, + F24 = sdl2::keyboard::Keycode::F24 as i32, + Execute = sdl2::keyboard::Keycode::Execute as i32, + Help = sdl2::keyboard::Keycode::Help as i32, + Menu = sdl2::keyboard::Keycode::Menu as i32, + Select = sdl2::keyboard::Keycode::Select as i32, + Stop = sdl2::keyboard::Keycode::Stop as i32, + Again = sdl2::keyboard::Keycode::Again as i32, + Undo = sdl2::keyboard::Keycode::Undo as i32, + Cut = sdl2::keyboard::Keycode::Cut as i32, + Copy = sdl2::keyboard::Keycode::Copy as i32, + Paste = sdl2::keyboard::Keycode::Paste as i32, + Find = sdl2::keyboard::Keycode::Find as i32, + Mute = sdl2::keyboard::Keycode::Mute as i32, + VolumeUp = sdl2::keyboard::Keycode::VolumeUp as i32, + VolumeDown = sdl2::keyboard::Keycode::VolumeDown as i32, + KpComma = sdl2::keyboard::Keycode::KpComma as i32, + KpEqualsAS400 = sdl2::keyboard::Keycode::KpEqualsAS400 as i32, + AltErase = sdl2::keyboard::Keycode::AltErase as i32, + Sysreq = sdl2::keyboard::Keycode::Sysreq as i32, + Cancel = sdl2::keyboard::Keycode::Cancel as i32, + Clear = sdl2::keyboard::Keycode::Clear as i32, + Prior = sdl2::keyboard::Keycode::Prior as i32, + Return2 = sdl2::keyboard::Keycode::Return2 as i32, + Separator = sdl2::keyboard::Keycode::Separator as i32, + Out = sdl2::keyboard::Keycode::Out as i32, + Oper = sdl2::keyboard::Keycode::Oper as i32, + ClearAgain = sdl2::keyboard::Keycode::ClearAgain as i32, + CrSel = sdl2::keyboard::Keycode::CrSel as i32, + ExSel = sdl2::keyboard::Keycode::ExSel as i32, + Kp00 = sdl2::keyboard::Keycode::Kp00 as i32, + Kp000 = sdl2::keyboard::Keycode::Kp000 as i32, + ThousandsSeparator = sdl2::keyboard::Keycode::ThousandsSeparator as i32, + DecimalSeparator = sdl2::keyboard::Keycode::DecimalSeparator as i32, + CurrencyUnit = sdl2::keyboard::Keycode::CurrencyUnit as i32, + CurrencySubUnit = sdl2::keyboard::Keycode::CurrencySubUnit as i32, + KpLeftParen = sdl2::keyboard::Keycode::KpLeftParen as i32, + KpRightParen = sdl2::keyboard::Keycode::KpRightParen as i32, + KpLeftBrace = sdl2::keyboard::Keycode::KpLeftBrace as i32, + KpRightBrace = sdl2::keyboard::Keycode::KpRightBrace as i32, + KpTab = sdl2::keyboard::Keycode::KpTab as i32, + KpBackspace = sdl2::keyboard::Keycode::KpBackspace as i32, + KpA = sdl2::keyboard::Keycode::KpA as i32, + KpB = sdl2::keyboard::Keycode::KpB as i32, + KpC = sdl2::keyboard::Keycode::KpC as i32, + KpD = sdl2::keyboard::Keycode::KpD as i32, + KpE = sdl2::keyboard::Keycode::KpE as i32, + KpF = sdl2::keyboard::Keycode::KpF as i32, + KpXor = sdl2::keyboard::Keycode::KpXor as i32, + KpPower = sdl2::keyboard::Keycode::KpPower as i32, + KpPercent = sdl2::keyboard::Keycode::KpPercent as i32, + KpLess = sdl2::keyboard::Keycode::KpLess as i32, + KpGreater = sdl2::keyboard::Keycode::KpGreater as i32, + KpAmpersand = sdl2::keyboard::Keycode::KpAmpersand as i32, + KpDblAmpersand = sdl2::keyboard::Keycode::KpDblAmpersand as i32, + KpVerticalBar = sdl2::keyboard::Keycode::KpVerticalBar as i32, + KpDblVerticalBar = sdl2::keyboard::Keycode::KpDblVerticalBar as i32, + KpColon = sdl2::keyboard::Keycode::KpColon as i32, + KpHash = sdl2::keyboard::Keycode::KpHash as i32, + KpSpace = sdl2::keyboard::Keycode::KpSpace as i32, + KpAt = sdl2::keyboard::Keycode::KpAt as i32, + KpExclam = sdl2::keyboard::Keycode::KpExclam as i32, + KpMemStore = sdl2::keyboard::Keycode::KpMemStore as i32, + KpMemRecall = sdl2::keyboard::Keycode::KpMemRecall as i32, + KpMemClear = sdl2::keyboard::Keycode::KpMemClear as i32, + KpMemAdd = sdl2::keyboard::Keycode::KpMemAdd as i32, + KpMemSubtract = sdl2::keyboard::Keycode::KpMemSubtract as i32, + KpMemMultiply = sdl2::keyboard::Keycode::KpMemMultiply as i32, + KpMemDivide = sdl2::keyboard::Keycode::KpMemDivide as i32, + KpPlusMinus = sdl2::keyboard::Keycode::KpPlusMinus as i32, + KpClear = sdl2::keyboard::Keycode::KpClear as i32, + KpClearEntry = sdl2::keyboard::Keycode::KpClearEntry as i32, + KpBinary = sdl2::keyboard::Keycode::KpBinary as i32, + KpOctal = sdl2::keyboard::Keycode::KpOctal as i32, + KpDecimal = sdl2::keyboard::Keycode::KpDecimal as i32, + KpHexadecimal = sdl2::keyboard::Keycode::KpHexadecimal as i32, + LCtrl = sdl2::keyboard::Keycode::LCtrl as i32, + LShift = sdl2::keyboard::Keycode::LShift as i32, + LAlt = sdl2::keyboard::Keycode::LAlt as i32, + LGui = sdl2::keyboard::Keycode::LGui as i32, + RCtrl = sdl2::keyboard::Keycode::RCtrl as i32, + RShift = sdl2::keyboard::Keycode::RShift as i32, + RAlt = sdl2::keyboard::Keycode::RAlt as i32, + RGui = sdl2::keyboard::Keycode::RGui as i32, + Mode = sdl2::keyboard::Keycode::Mode as i32, + AudioNext = sdl2::keyboard::Keycode::AudioNext as i32, + AudioPrev = sdl2::keyboard::Keycode::AudioPrev as i32, + AudioStop = sdl2::keyboard::Keycode::AudioStop as i32, + AudioPlay = sdl2::keyboard::Keycode::AudioPlay as i32, + AudioMute = sdl2::keyboard::Keycode::AudioMute as i32, + MediaSelect = sdl2::keyboard::Keycode::MediaSelect as i32, + Www = sdl2::keyboard::Keycode::Www as i32, + Mail = sdl2::keyboard::Keycode::Mail as i32, + Calculator = sdl2::keyboard::Keycode::Calculator as i32, + Computer = sdl2::keyboard::Keycode::Computer as i32, + AcSearch = sdl2::keyboard::Keycode::AcSearch as i32, + AcHome = sdl2::keyboard::Keycode::AcHome as i32, + AcBack = sdl2::keyboard::Keycode::AcBack as i32, + AcForward = sdl2::keyboard::Keycode::AcForward as i32, + AcStop = sdl2::keyboard::Keycode::AcStop as i32, + AcRefresh = sdl2::keyboard::Keycode::AcRefresh as i32, + AcBookmarks = sdl2::keyboard::Keycode::AcBookmarks as i32, + BrightnessDown = sdl2::keyboard::Keycode::BrightnessDown as i32, + BrightnessUp = sdl2::keyboard::Keycode::BrightnessUp as i32, + DisplaySwitch = sdl2::keyboard::Keycode::DisplaySwitch as i32, + KbdIllumToggle = sdl2::keyboard::Keycode::KbdIllumToggle as i32, + KbdIllumDown = sdl2::keyboard::Keycode::KbdIllumDown as i32, + KbdIllumUp = sdl2::keyboard::Keycode::KbdIllumUp as i32, + Eject = sdl2::keyboard::Keycode::Eject as i32, + Sleep = sdl2::keyboard::Keycode::Sleep as i32, } impl From for Keycode { - fn from(value: sdl2::keyboard::Keycode) -> Self { - match value { - sdl2::keyboard::Keycode::Backspace => Keycode::Backspace, - sdl2::keyboard::Keycode::Tab => Keycode::Tab, - sdl2::keyboard::Keycode::Return => Keycode::Return, - sdl2::keyboard::Keycode::Escape => Keycode::Escape, - sdl2::keyboard::Keycode::Space => Keycode::Space, - sdl2::keyboard::Keycode::Exclaim => Keycode::Exclaim, - sdl2::keyboard::Keycode::Quotedbl => Keycode::Quotedbl, - sdl2::keyboard::Keycode::Hash => Keycode::Hash, - sdl2::keyboard::Keycode::Dollar => Keycode::Dollar, - sdl2::keyboard::Keycode::Percent => Keycode::Percent, - sdl2::keyboard::Keycode::Ampersand => Keycode::Ampersand, - sdl2::keyboard::Keycode::Quote => Keycode::Quote, - sdl2::keyboard::Keycode::LeftParen => Keycode::LeftParen, - sdl2::keyboard::Keycode::RightParen => Keycode::RightParen, - sdl2::keyboard::Keycode::Asterisk => Keycode::Asterisk, - sdl2::keyboard::Keycode::Plus => Keycode::Plus, - sdl2::keyboard::Keycode::Comma => Keycode::Comma, - sdl2::keyboard::Keycode::Minus => Keycode::Minus, - sdl2::keyboard::Keycode::Period => Keycode::Period, - sdl2::keyboard::Keycode::Slash => Keycode::Slash, - sdl2::keyboard::Keycode::Num0 => Keycode::Num0, - sdl2::keyboard::Keycode::Num1 => Keycode::Num1, - sdl2::keyboard::Keycode::Num2 => Keycode::Num2, - sdl2::keyboard::Keycode::Num3 => Keycode::Num3, - sdl2::keyboard::Keycode::Num4 => Keycode::Num4, - sdl2::keyboard::Keycode::Num5 => Keycode::Num5, - sdl2::keyboard::Keycode::Num6 => Keycode::Num6, - sdl2::keyboard::Keycode::Num7 => Keycode::Num7, - sdl2::keyboard::Keycode::Num8 => Keycode::Num8, - sdl2::keyboard::Keycode::Num9 => Keycode::Num9, - sdl2::keyboard::Keycode::Colon => Keycode::Colon, - sdl2::keyboard::Keycode::Semicolon => Keycode::Semicolon, - sdl2::keyboard::Keycode::Less => Keycode::Less, - sdl2::keyboard::Keycode::Equals => Keycode::Equals, - sdl2::keyboard::Keycode::Greater => Keycode::Greater, - sdl2::keyboard::Keycode::Question => Keycode::Question, - sdl2::keyboard::Keycode::At => Keycode::At, - sdl2::keyboard::Keycode::LeftBracket => Keycode::LeftBracket, - sdl2::keyboard::Keycode::Backslash => Keycode::Backslash, - sdl2::keyboard::Keycode::RightBracket => Keycode::RightBracket, - sdl2::keyboard::Keycode::Caret => Keycode::Caret, - sdl2::keyboard::Keycode::Underscore => Keycode::Underscore, - sdl2::keyboard::Keycode::Backquote => Keycode::Backquote, - sdl2::keyboard::Keycode::A => Keycode::A, - sdl2::keyboard::Keycode::B => Keycode::B, - sdl2::keyboard::Keycode::C => Keycode::C, - sdl2::keyboard::Keycode::D => Keycode::D, - sdl2::keyboard::Keycode::E => Keycode::E, - sdl2::keyboard::Keycode::F => Keycode::F, - sdl2::keyboard::Keycode::G => Keycode::G, - sdl2::keyboard::Keycode::H => Keycode::H, - sdl2::keyboard::Keycode::I => Keycode::I, - sdl2::keyboard::Keycode::J => Keycode::J, - sdl2::keyboard::Keycode::K => Keycode::K, - sdl2::keyboard::Keycode::L => Keycode::L, - sdl2::keyboard::Keycode::M => Keycode::M, - sdl2::keyboard::Keycode::N => Keycode::N, - sdl2::keyboard::Keycode::O => Keycode::O, - sdl2::keyboard::Keycode::P => Keycode::P, - sdl2::keyboard::Keycode::Q => Keycode::Q, - sdl2::keyboard::Keycode::R => Keycode::R, - sdl2::keyboard::Keycode::S => Keycode::S, - sdl2::keyboard::Keycode::T => Keycode::T, - sdl2::keyboard::Keycode::U => Keycode::U, - sdl2::keyboard::Keycode::V => Keycode::V, - sdl2::keyboard::Keycode::W => Keycode::W, - sdl2::keyboard::Keycode::X => Keycode::X, - sdl2::keyboard::Keycode::Y => Keycode::Y, - sdl2::keyboard::Keycode::Z => Keycode::Z, - sdl2::keyboard::Keycode::Delete => Keycode::Delete, - sdl2::keyboard::Keycode::CapsLock => Keycode::CapsLock, - sdl2::keyboard::Keycode::F1 => Keycode::F1, - sdl2::keyboard::Keycode::F2 => Keycode::F2, - sdl2::keyboard::Keycode::F3 => Keycode::F3, - sdl2::keyboard::Keycode::F4 => Keycode::F4, - sdl2::keyboard::Keycode::F5 => Keycode::F5, - sdl2::keyboard::Keycode::F6 => Keycode::F6, - sdl2::keyboard::Keycode::F7 => Keycode::F7, - sdl2::keyboard::Keycode::F8 => Keycode::F8, - sdl2::keyboard::Keycode::F9 => Keycode::F9, - sdl2::keyboard::Keycode::F10 => Keycode::F10, - sdl2::keyboard::Keycode::F11 => Keycode::F11, - sdl2::keyboard::Keycode::F12 => Keycode::F12, - sdl2::keyboard::Keycode::PrintScreen => Keycode::PrintScreen, - sdl2::keyboard::Keycode::ScrollLock => Keycode::ScrollLock, - sdl2::keyboard::Keycode::Pause => Keycode::Pause, - sdl2::keyboard::Keycode::Insert => Keycode::Insert, - sdl2::keyboard::Keycode::Home => Keycode::Home, - sdl2::keyboard::Keycode::PageUp => Keycode::PageUp, - sdl2::keyboard::Keycode::End => Keycode::End, - sdl2::keyboard::Keycode::PageDown => Keycode::PageDown, - sdl2::keyboard::Keycode::Right => Keycode::Right, - sdl2::keyboard::Keycode::Left => Keycode::Left, - sdl2::keyboard::Keycode::Down => Keycode::Down, - sdl2::keyboard::Keycode::Up => Keycode::Up, - sdl2::keyboard::Keycode::NumLockClear => Keycode::NumLockClear, - sdl2::keyboard::Keycode::KpDivide => Keycode::KpDivide, - sdl2::keyboard::Keycode::KpMultiply => Keycode::KpMultiply, - sdl2::keyboard::Keycode::KpMinus => Keycode::KpMinus, - sdl2::keyboard::Keycode::KpPlus => Keycode::KpPlus, - sdl2::keyboard::Keycode::KpEnter => Keycode::KpEnter, - sdl2::keyboard::Keycode::Kp1 => Keycode::Kp1, - sdl2::keyboard::Keycode::Kp2 => Keycode::Kp2, - sdl2::keyboard::Keycode::Kp3 => Keycode::Kp3, - sdl2::keyboard::Keycode::Kp4 => Keycode::Kp4, - sdl2::keyboard::Keycode::Kp5 => Keycode::Kp5, - sdl2::keyboard::Keycode::Kp6 => Keycode::Kp6, - sdl2::keyboard::Keycode::Kp7 => Keycode::Kp7, - sdl2::keyboard::Keycode::Kp8 => Keycode::Kp8, - sdl2::keyboard::Keycode::Kp9 => Keycode::Kp9, - sdl2::keyboard::Keycode::Kp0 => Keycode::Kp0, - sdl2::keyboard::Keycode::KpPeriod => Keycode::KpPeriod, - sdl2::keyboard::Keycode::Application => Keycode::Application, - sdl2::keyboard::Keycode::Power => Keycode::Power, - sdl2::keyboard::Keycode::KpEquals => Keycode::KpEquals, - sdl2::keyboard::Keycode::F13 => Keycode::F13, - sdl2::keyboard::Keycode::F14 => Keycode::F14, - sdl2::keyboard::Keycode::F15 => Keycode::F15, - sdl2::keyboard::Keycode::F16 => Keycode::F16, - sdl2::keyboard::Keycode::F17 => Keycode::F17, - sdl2::keyboard::Keycode::F18 => Keycode::F18, - sdl2::keyboard::Keycode::F19 => Keycode::F19, - sdl2::keyboard::Keycode::F20 => Keycode::F20, - sdl2::keyboard::Keycode::F21 => Keycode::F21, - sdl2::keyboard::Keycode::F22 => Keycode::F22, - sdl2::keyboard::Keycode::F23 => Keycode::F23, - sdl2::keyboard::Keycode::F24 => Keycode::F24, - sdl2::keyboard::Keycode::Execute => Keycode::Execute, - sdl2::keyboard::Keycode::Help => Keycode::Help, - sdl2::keyboard::Keycode::Menu => Keycode::Menu, - sdl2::keyboard::Keycode::Select => Keycode::Select, - sdl2::keyboard::Keycode::Stop => Keycode::Stop, - sdl2::keyboard::Keycode::Again => Keycode::Again, - sdl2::keyboard::Keycode::Undo => Keycode::Undo, - sdl2::keyboard::Keycode::Cut => Keycode::Cut, - sdl2::keyboard::Keycode::Copy => Keycode::Copy, - sdl2::keyboard::Keycode::Paste => Keycode::Paste, - sdl2::keyboard::Keycode::Find => Keycode::Find, - sdl2::keyboard::Keycode::Mute => Keycode::Mute, - sdl2::keyboard::Keycode::VolumeUp => Keycode::VolumeUp, - sdl2::keyboard::Keycode::VolumeDown => Keycode::VolumeDown, - sdl2::keyboard::Keycode::KpComma => Keycode::KpComma, - sdl2::keyboard::Keycode::KpEqualsAS400 => Keycode::KpEqualsAS400, - sdl2::keyboard::Keycode::AltErase => Keycode::AltErase, - sdl2::keyboard::Keycode::Sysreq => Keycode::Sysreq, - sdl2::keyboard::Keycode::Cancel => Keycode::Cancel, - sdl2::keyboard::Keycode::Clear => Keycode::Clear, - sdl2::keyboard::Keycode::Prior => Keycode::Prior, - sdl2::keyboard::Keycode::Return2 => Keycode::Return2, - sdl2::keyboard::Keycode::Separator => Keycode::Separator, - sdl2::keyboard::Keycode::Out => Keycode::Out, - sdl2::keyboard::Keycode::Oper => Keycode::Oper, - sdl2::keyboard::Keycode::ClearAgain => Keycode::ClearAgain, - sdl2::keyboard::Keycode::CrSel => Keycode::CrSel, - sdl2::keyboard::Keycode::ExSel => Keycode::ExSel, - sdl2::keyboard::Keycode::Kp00 => Keycode::Kp00, - sdl2::keyboard::Keycode::Kp000 => Keycode::Kp000, - sdl2::keyboard::Keycode::ThousandsSeparator => Keycode::ThousandsSeparator, - sdl2::keyboard::Keycode::DecimalSeparator => Keycode::DecimalSeparator, - sdl2::keyboard::Keycode::CurrencyUnit => Keycode::CurrencyUnit, - sdl2::keyboard::Keycode::CurrencySubUnit => Keycode::CurrencySubUnit, - sdl2::keyboard::Keycode::KpLeftParen => Keycode::KpLeftParen, - sdl2::keyboard::Keycode::KpRightParen => Keycode::KpRightParen, - sdl2::keyboard::Keycode::KpLeftBrace => Keycode::KpLeftBrace, - sdl2::keyboard::Keycode::KpRightBrace => Keycode::KpRightBrace, - sdl2::keyboard::Keycode::KpTab => Keycode::KpTab, - sdl2::keyboard::Keycode::KpBackspace => Keycode::KpBackspace, - sdl2::keyboard::Keycode::KpA => Keycode::KpA, - sdl2::keyboard::Keycode::KpB => Keycode::KpB, - sdl2::keyboard::Keycode::KpC => Keycode::KpC, - sdl2::keyboard::Keycode::KpD => Keycode::KpD, - sdl2::keyboard::Keycode::KpE => Keycode::KpE, - sdl2::keyboard::Keycode::KpF => Keycode::KpF, - sdl2::keyboard::Keycode::KpXor => Keycode::KpXor, - sdl2::keyboard::Keycode::KpPower => Keycode::KpPower, - sdl2::keyboard::Keycode::KpPercent => Keycode::KpPercent, - sdl2::keyboard::Keycode::KpLess => Keycode::KpLess, - sdl2::keyboard::Keycode::KpGreater => Keycode::KpGreater, - sdl2::keyboard::Keycode::KpAmpersand => Keycode::KpAmpersand, - sdl2::keyboard::Keycode::KpDblAmpersand => Keycode::KpDblAmpersand, - sdl2::keyboard::Keycode::KpVerticalBar => Keycode::KpVerticalBar, - sdl2::keyboard::Keycode::KpDblVerticalBar => Keycode::KpDblVerticalBar, - sdl2::keyboard::Keycode::KpColon => Keycode::KpColon, - sdl2::keyboard::Keycode::KpHash => Keycode::KpHash, - sdl2::keyboard::Keycode::KpSpace => Keycode::KpSpace, - sdl2::keyboard::Keycode::KpAt => Keycode::KpAt, - sdl2::keyboard::Keycode::KpExclam => Keycode::KpExclam, - sdl2::keyboard::Keycode::KpMemStore => Keycode::KpMemStore, - sdl2::keyboard::Keycode::KpMemRecall => Keycode::KpMemRecall, - sdl2::keyboard::Keycode::KpMemClear => Keycode::KpMemClear, - sdl2::keyboard::Keycode::KpMemAdd => Keycode::KpMemAdd, - sdl2::keyboard::Keycode::KpMemSubtract => Keycode::KpMemSubtract, - sdl2::keyboard::Keycode::KpMemMultiply => Keycode::KpMemMultiply, - sdl2::keyboard::Keycode::KpMemDivide => Keycode::KpMemDivide, - sdl2::keyboard::Keycode::KpPlusMinus => Keycode::KpPlusMinus, - sdl2::keyboard::Keycode::KpClear => Keycode::KpClear, - sdl2::keyboard::Keycode::KpClearEntry => Keycode::KpClearEntry, - sdl2::keyboard::Keycode::KpBinary => Keycode::KpBinary, - sdl2::keyboard::Keycode::KpOctal => Keycode::KpOctal, - sdl2::keyboard::Keycode::KpDecimal => Keycode::KpDecimal, - sdl2::keyboard::Keycode::KpHexadecimal => Keycode::KpHexadecimal, - sdl2::keyboard::Keycode::LCtrl => Keycode::LCtrl, - sdl2::keyboard::Keycode::LShift => Keycode::LShift, - sdl2::keyboard::Keycode::LAlt => Keycode::LAlt, - sdl2::keyboard::Keycode::LGui => Keycode::LGui, - sdl2::keyboard::Keycode::RCtrl => Keycode::RCtrl, - sdl2::keyboard::Keycode::RShift => Keycode::RShift, - sdl2::keyboard::Keycode::RAlt => Keycode::RAlt, - sdl2::keyboard::Keycode::RGui => Keycode::RGui, - sdl2::keyboard::Keycode::Mode => Keycode::Mode, - sdl2::keyboard::Keycode::AudioNext => Keycode::AudioNext, - sdl2::keyboard::Keycode::AudioPrev => Keycode::AudioPrev, - sdl2::keyboard::Keycode::AudioStop => Keycode::AudioStop, - sdl2::keyboard::Keycode::AudioPlay => Keycode::AudioPlay, - sdl2::keyboard::Keycode::AudioMute => Keycode::AudioMute, - sdl2::keyboard::Keycode::MediaSelect => Keycode::MediaSelect, - sdl2::keyboard::Keycode::Www => Keycode::Www, - sdl2::keyboard::Keycode::Mail => Keycode::Mail, - sdl2::keyboard::Keycode::Calculator => Keycode::Calculator, - sdl2::keyboard::Keycode::Computer => Keycode::Computer, - sdl2::keyboard::Keycode::AcSearch => Keycode::AcSearch, - sdl2::keyboard::Keycode::AcHome => Keycode::AcHome, - sdl2::keyboard::Keycode::AcBack => Keycode::AcBack, - sdl2::keyboard::Keycode::AcForward => Keycode::AcForward, - sdl2::keyboard::Keycode::AcStop => Keycode::AcStop, - sdl2::keyboard::Keycode::AcRefresh => Keycode::AcRefresh, - sdl2::keyboard::Keycode::AcBookmarks => Keycode::AcBookmarks, - sdl2::keyboard::Keycode::BrightnessDown => Keycode::BrightnessDown, - sdl2::keyboard::Keycode::BrightnessUp => Keycode::BrightnessUp, - sdl2::keyboard::Keycode::DisplaySwitch => Keycode::DisplaySwitch, - sdl2::keyboard::Keycode::KbdIllumToggle => Keycode::KbdIllumToggle, - sdl2::keyboard::Keycode::KbdIllumDown => Keycode::KbdIllumDown, - sdl2::keyboard::Keycode::KbdIllumUp => Keycode::KbdIllumUp, - sdl2::keyboard::Keycode::Eject => Keycode::Eject, - sdl2::keyboard::Keycode::Sleep => Keycode::Sleep, - } - } + fn from(value: sdl2::keyboard::Keycode) -> Self { + match value { + sdl2::keyboard::Keycode::Backspace => Keycode::Backspace, + sdl2::keyboard::Keycode::Tab => Keycode::Tab, + sdl2::keyboard::Keycode::Return => Keycode::Return, + sdl2::keyboard::Keycode::Escape => Keycode::Escape, + sdl2::keyboard::Keycode::Space => Keycode::Space, + sdl2::keyboard::Keycode::Exclaim => Keycode::Exclaim, + sdl2::keyboard::Keycode::Quotedbl => Keycode::Quotedbl, + sdl2::keyboard::Keycode::Hash => Keycode::Hash, + sdl2::keyboard::Keycode::Dollar => Keycode::Dollar, + sdl2::keyboard::Keycode::Percent => Keycode::Percent, + sdl2::keyboard::Keycode::Ampersand => Keycode::Ampersand, + sdl2::keyboard::Keycode::Quote => Keycode::Quote, + sdl2::keyboard::Keycode::LeftParen => Keycode::LeftParen, + sdl2::keyboard::Keycode::RightParen => Keycode::RightParen, + sdl2::keyboard::Keycode::Asterisk => Keycode::Asterisk, + sdl2::keyboard::Keycode::Plus => Keycode::Plus, + sdl2::keyboard::Keycode::Comma => Keycode::Comma, + sdl2::keyboard::Keycode::Minus => Keycode::Minus, + sdl2::keyboard::Keycode::Period => Keycode::Period, + sdl2::keyboard::Keycode::Slash => Keycode::Slash, + sdl2::keyboard::Keycode::Num0 => Keycode::Num0, + sdl2::keyboard::Keycode::Num1 => Keycode::Num1, + sdl2::keyboard::Keycode::Num2 => Keycode::Num2, + sdl2::keyboard::Keycode::Num3 => Keycode::Num3, + sdl2::keyboard::Keycode::Num4 => Keycode::Num4, + sdl2::keyboard::Keycode::Num5 => Keycode::Num5, + sdl2::keyboard::Keycode::Num6 => Keycode::Num6, + sdl2::keyboard::Keycode::Num7 => Keycode::Num7, + sdl2::keyboard::Keycode::Num8 => Keycode::Num8, + sdl2::keyboard::Keycode::Num9 => Keycode::Num9, + sdl2::keyboard::Keycode::Colon => Keycode::Colon, + sdl2::keyboard::Keycode::Semicolon => Keycode::Semicolon, + sdl2::keyboard::Keycode::Less => Keycode::Less, + sdl2::keyboard::Keycode::Equals => Keycode::Equals, + sdl2::keyboard::Keycode::Greater => Keycode::Greater, + sdl2::keyboard::Keycode::Question => Keycode::Question, + sdl2::keyboard::Keycode::At => Keycode::At, + sdl2::keyboard::Keycode::LeftBracket => Keycode::LeftBracket, + sdl2::keyboard::Keycode::Backslash => Keycode::Backslash, + sdl2::keyboard::Keycode::RightBracket => Keycode::RightBracket, + sdl2::keyboard::Keycode::Caret => Keycode::Caret, + sdl2::keyboard::Keycode::Underscore => Keycode::Underscore, + sdl2::keyboard::Keycode::Backquote => Keycode::Backquote, + sdl2::keyboard::Keycode::A => Keycode::A, + sdl2::keyboard::Keycode::B => Keycode::B, + sdl2::keyboard::Keycode::C => Keycode::C, + sdl2::keyboard::Keycode::D => Keycode::D, + sdl2::keyboard::Keycode::E => Keycode::E, + sdl2::keyboard::Keycode::F => Keycode::F, + sdl2::keyboard::Keycode::G => Keycode::G, + sdl2::keyboard::Keycode::H => Keycode::H, + sdl2::keyboard::Keycode::I => Keycode::I, + sdl2::keyboard::Keycode::J => Keycode::J, + sdl2::keyboard::Keycode::K => Keycode::K, + sdl2::keyboard::Keycode::L => Keycode::L, + sdl2::keyboard::Keycode::M => Keycode::M, + sdl2::keyboard::Keycode::N => Keycode::N, + sdl2::keyboard::Keycode::O => Keycode::O, + sdl2::keyboard::Keycode::P => Keycode::P, + sdl2::keyboard::Keycode::Q => Keycode::Q, + sdl2::keyboard::Keycode::R => Keycode::R, + sdl2::keyboard::Keycode::S => Keycode::S, + sdl2::keyboard::Keycode::T => Keycode::T, + sdl2::keyboard::Keycode::U => Keycode::U, + sdl2::keyboard::Keycode::V => Keycode::V, + sdl2::keyboard::Keycode::W => Keycode::W, + sdl2::keyboard::Keycode::X => Keycode::X, + sdl2::keyboard::Keycode::Y => Keycode::Y, + sdl2::keyboard::Keycode::Z => Keycode::Z, + sdl2::keyboard::Keycode::Delete => Keycode::Delete, + sdl2::keyboard::Keycode::CapsLock => Keycode::CapsLock, + sdl2::keyboard::Keycode::F1 => Keycode::F1, + sdl2::keyboard::Keycode::F2 => Keycode::F2, + sdl2::keyboard::Keycode::F3 => Keycode::F3, + sdl2::keyboard::Keycode::F4 => Keycode::F4, + sdl2::keyboard::Keycode::F5 => Keycode::F5, + sdl2::keyboard::Keycode::F6 => Keycode::F6, + sdl2::keyboard::Keycode::F7 => Keycode::F7, + sdl2::keyboard::Keycode::F8 => Keycode::F8, + sdl2::keyboard::Keycode::F9 => Keycode::F9, + sdl2::keyboard::Keycode::F10 => Keycode::F10, + sdl2::keyboard::Keycode::F11 => Keycode::F11, + sdl2::keyboard::Keycode::F12 => Keycode::F12, + sdl2::keyboard::Keycode::PrintScreen => Keycode::PrintScreen, + sdl2::keyboard::Keycode::ScrollLock => Keycode::ScrollLock, + sdl2::keyboard::Keycode::Pause => Keycode::Pause, + sdl2::keyboard::Keycode::Insert => Keycode::Insert, + sdl2::keyboard::Keycode::Home => Keycode::Home, + sdl2::keyboard::Keycode::PageUp => Keycode::PageUp, + sdl2::keyboard::Keycode::End => Keycode::End, + sdl2::keyboard::Keycode::PageDown => Keycode::PageDown, + sdl2::keyboard::Keycode::Right => Keycode::Right, + sdl2::keyboard::Keycode::Left => Keycode::Left, + sdl2::keyboard::Keycode::Down => Keycode::Down, + sdl2::keyboard::Keycode::Up => Keycode::Up, + sdl2::keyboard::Keycode::NumLockClear => Keycode::NumLockClear, + sdl2::keyboard::Keycode::KpDivide => Keycode::KpDivide, + sdl2::keyboard::Keycode::KpMultiply => Keycode::KpMultiply, + sdl2::keyboard::Keycode::KpMinus => Keycode::KpMinus, + sdl2::keyboard::Keycode::KpPlus => Keycode::KpPlus, + sdl2::keyboard::Keycode::KpEnter => Keycode::KpEnter, + sdl2::keyboard::Keycode::Kp1 => Keycode::Kp1, + sdl2::keyboard::Keycode::Kp2 => Keycode::Kp2, + sdl2::keyboard::Keycode::Kp3 => Keycode::Kp3, + sdl2::keyboard::Keycode::Kp4 => Keycode::Kp4, + sdl2::keyboard::Keycode::Kp5 => Keycode::Kp5, + sdl2::keyboard::Keycode::Kp6 => Keycode::Kp6, + sdl2::keyboard::Keycode::Kp7 => Keycode::Kp7, + sdl2::keyboard::Keycode::Kp8 => Keycode::Kp8, + sdl2::keyboard::Keycode::Kp9 => Keycode::Kp9, + sdl2::keyboard::Keycode::Kp0 => Keycode::Kp0, + sdl2::keyboard::Keycode::KpPeriod => Keycode::KpPeriod, + sdl2::keyboard::Keycode::Application => Keycode::Application, + sdl2::keyboard::Keycode::Power => Keycode::Power, + sdl2::keyboard::Keycode::KpEquals => Keycode::KpEquals, + sdl2::keyboard::Keycode::F13 => Keycode::F13, + sdl2::keyboard::Keycode::F14 => Keycode::F14, + sdl2::keyboard::Keycode::F15 => Keycode::F15, + sdl2::keyboard::Keycode::F16 => Keycode::F16, + sdl2::keyboard::Keycode::F17 => Keycode::F17, + sdl2::keyboard::Keycode::F18 => Keycode::F18, + sdl2::keyboard::Keycode::F19 => Keycode::F19, + sdl2::keyboard::Keycode::F20 => Keycode::F20, + sdl2::keyboard::Keycode::F21 => Keycode::F21, + sdl2::keyboard::Keycode::F22 => Keycode::F22, + sdl2::keyboard::Keycode::F23 => Keycode::F23, + sdl2::keyboard::Keycode::F24 => Keycode::F24, + sdl2::keyboard::Keycode::Execute => Keycode::Execute, + sdl2::keyboard::Keycode::Help => Keycode::Help, + sdl2::keyboard::Keycode::Menu => Keycode::Menu, + sdl2::keyboard::Keycode::Select => Keycode::Select, + sdl2::keyboard::Keycode::Stop => Keycode::Stop, + sdl2::keyboard::Keycode::Again => Keycode::Again, + sdl2::keyboard::Keycode::Undo => Keycode::Undo, + sdl2::keyboard::Keycode::Cut => Keycode::Cut, + sdl2::keyboard::Keycode::Copy => Keycode::Copy, + sdl2::keyboard::Keycode::Paste => Keycode::Paste, + sdl2::keyboard::Keycode::Find => Keycode::Find, + sdl2::keyboard::Keycode::Mute => Keycode::Mute, + sdl2::keyboard::Keycode::VolumeUp => Keycode::VolumeUp, + sdl2::keyboard::Keycode::VolumeDown => Keycode::VolumeDown, + sdl2::keyboard::Keycode::KpComma => Keycode::KpComma, + sdl2::keyboard::Keycode::KpEqualsAS400 => Keycode::KpEqualsAS400, + sdl2::keyboard::Keycode::AltErase => Keycode::AltErase, + sdl2::keyboard::Keycode::Sysreq => Keycode::Sysreq, + sdl2::keyboard::Keycode::Cancel => Keycode::Cancel, + sdl2::keyboard::Keycode::Clear => Keycode::Clear, + sdl2::keyboard::Keycode::Prior => Keycode::Prior, + sdl2::keyboard::Keycode::Return2 => Keycode::Return2, + sdl2::keyboard::Keycode::Separator => Keycode::Separator, + sdl2::keyboard::Keycode::Out => Keycode::Out, + sdl2::keyboard::Keycode::Oper => Keycode::Oper, + sdl2::keyboard::Keycode::ClearAgain => Keycode::ClearAgain, + sdl2::keyboard::Keycode::CrSel => Keycode::CrSel, + sdl2::keyboard::Keycode::ExSel => Keycode::ExSel, + sdl2::keyboard::Keycode::Kp00 => Keycode::Kp00, + sdl2::keyboard::Keycode::Kp000 => Keycode::Kp000, + sdl2::keyboard::Keycode::ThousandsSeparator => Keycode::ThousandsSeparator, + sdl2::keyboard::Keycode::DecimalSeparator => Keycode::DecimalSeparator, + sdl2::keyboard::Keycode::CurrencyUnit => Keycode::CurrencyUnit, + sdl2::keyboard::Keycode::CurrencySubUnit => Keycode::CurrencySubUnit, + sdl2::keyboard::Keycode::KpLeftParen => Keycode::KpLeftParen, + sdl2::keyboard::Keycode::KpRightParen => Keycode::KpRightParen, + sdl2::keyboard::Keycode::KpLeftBrace => Keycode::KpLeftBrace, + sdl2::keyboard::Keycode::KpRightBrace => Keycode::KpRightBrace, + sdl2::keyboard::Keycode::KpTab => Keycode::KpTab, + sdl2::keyboard::Keycode::KpBackspace => Keycode::KpBackspace, + sdl2::keyboard::Keycode::KpA => Keycode::KpA, + sdl2::keyboard::Keycode::KpB => Keycode::KpB, + sdl2::keyboard::Keycode::KpC => Keycode::KpC, + sdl2::keyboard::Keycode::KpD => Keycode::KpD, + sdl2::keyboard::Keycode::KpE => Keycode::KpE, + sdl2::keyboard::Keycode::KpF => Keycode::KpF, + sdl2::keyboard::Keycode::KpXor => Keycode::KpXor, + sdl2::keyboard::Keycode::KpPower => Keycode::KpPower, + sdl2::keyboard::Keycode::KpPercent => Keycode::KpPercent, + sdl2::keyboard::Keycode::KpLess => Keycode::KpLess, + sdl2::keyboard::Keycode::KpGreater => Keycode::KpGreater, + sdl2::keyboard::Keycode::KpAmpersand => Keycode::KpAmpersand, + sdl2::keyboard::Keycode::KpDblAmpersand => Keycode::KpDblAmpersand, + sdl2::keyboard::Keycode::KpVerticalBar => Keycode::KpVerticalBar, + sdl2::keyboard::Keycode::KpDblVerticalBar => Keycode::KpDblVerticalBar, + sdl2::keyboard::Keycode::KpColon => Keycode::KpColon, + sdl2::keyboard::Keycode::KpHash => Keycode::KpHash, + sdl2::keyboard::Keycode::KpSpace => Keycode::KpSpace, + sdl2::keyboard::Keycode::KpAt => Keycode::KpAt, + sdl2::keyboard::Keycode::KpExclam => Keycode::KpExclam, + sdl2::keyboard::Keycode::KpMemStore => Keycode::KpMemStore, + sdl2::keyboard::Keycode::KpMemRecall => Keycode::KpMemRecall, + sdl2::keyboard::Keycode::KpMemClear => Keycode::KpMemClear, + sdl2::keyboard::Keycode::KpMemAdd => Keycode::KpMemAdd, + sdl2::keyboard::Keycode::KpMemSubtract => Keycode::KpMemSubtract, + sdl2::keyboard::Keycode::KpMemMultiply => Keycode::KpMemMultiply, + sdl2::keyboard::Keycode::KpMemDivide => Keycode::KpMemDivide, + sdl2::keyboard::Keycode::KpPlusMinus => Keycode::KpPlusMinus, + sdl2::keyboard::Keycode::KpClear => Keycode::KpClear, + sdl2::keyboard::Keycode::KpClearEntry => Keycode::KpClearEntry, + sdl2::keyboard::Keycode::KpBinary => Keycode::KpBinary, + sdl2::keyboard::Keycode::KpOctal => Keycode::KpOctal, + sdl2::keyboard::Keycode::KpDecimal => Keycode::KpDecimal, + sdl2::keyboard::Keycode::KpHexadecimal => Keycode::KpHexadecimal, + sdl2::keyboard::Keycode::LCtrl => Keycode::LCtrl, + sdl2::keyboard::Keycode::LShift => Keycode::LShift, + sdl2::keyboard::Keycode::LAlt => Keycode::LAlt, + sdl2::keyboard::Keycode::LGui => Keycode::LGui, + sdl2::keyboard::Keycode::RCtrl => Keycode::RCtrl, + sdl2::keyboard::Keycode::RShift => Keycode::RShift, + sdl2::keyboard::Keycode::RAlt => Keycode::RAlt, + sdl2::keyboard::Keycode::RGui => Keycode::RGui, + sdl2::keyboard::Keycode::Mode => Keycode::Mode, + sdl2::keyboard::Keycode::AudioNext => Keycode::AudioNext, + sdl2::keyboard::Keycode::AudioPrev => Keycode::AudioPrev, + sdl2::keyboard::Keycode::AudioStop => Keycode::AudioStop, + sdl2::keyboard::Keycode::AudioPlay => Keycode::AudioPlay, + sdl2::keyboard::Keycode::AudioMute => Keycode::AudioMute, + sdl2::keyboard::Keycode::MediaSelect => Keycode::MediaSelect, + sdl2::keyboard::Keycode::Www => Keycode::Www, + sdl2::keyboard::Keycode::Mail => Keycode::Mail, + sdl2::keyboard::Keycode::Calculator => Keycode::Calculator, + sdl2::keyboard::Keycode::Computer => Keycode::Computer, + sdl2::keyboard::Keycode::AcSearch => Keycode::AcSearch, + sdl2::keyboard::Keycode::AcHome => Keycode::AcHome, + sdl2::keyboard::Keycode::AcBack => Keycode::AcBack, + sdl2::keyboard::Keycode::AcForward => Keycode::AcForward, + sdl2::keyboard::Keycode::AcStop => Keycode::AcStop, + sdl2::keyboard::Keycode::AcRefresh => Keycode::AcRefresh, + sdl2::keyboard::Keycode::AcBookmarks => Keycode::AcBookmarks, + sdl2::keyboard::Keycode::BrightnessDown => Keycode::BrightnessDown, + sdl2::keyboard::Keycode::BrightnessUp => Keycode::BrightnessUp, + sdl2::keyboard::Keycode::DisplaySwitch => Keycode::DisplaySwitch, + sdl2::keyboard::Keycode::KbdIllumToggle => Keycode::KbdIllumToggle, + sdl2::keyboard::Keycode::KbdIllumDown => Keycode::KbdIllumDown, + sdl2::keyboard::Keycode::KbdIllumUp => Keycode::KbdIllumUp, + sdl2::keyboard::Keycode::Eject => Keycode::Eject, + sdl2::keyboard::Keycode::Sleep => Keycode::Sleep, + } + } } \ No newline at end of file diff --git a/libretrogd/src/system/input_devices/keyboard/mod.rs b/libretrogd/src/system/input_devices/keyboard/mod.rs index 4d1b4f6..4221062 100644 --- a/libretrogd/src/system/input_devices/keyboard/mod.rs +++ b/libretrogd/src/system/input_devices/keyboard/mod.rs @@ -19,85 +19,85 @@ const MAX_KEYS: usize = 256; /// [`System`]: crate::System #[derive(Debug)] pub struct Keyboard { - keyboard: [ButtonState; MAX_KEYS], // Box<[ButtonState]>, + keyboard: [ButtonState; MAX_KEYS], // Box<[ButtonState]>, } impl Keyboard { - pub fn new() -> Keyboard { - Keyboard { - keyboard: [ButtonState::Idle; MAX_KEYS], - } - /* - Keyboard { - keyboard: vec![ButtonState::Idle; 256].into_boxed_slice(), - } - */ - } + pub fn new() -> Keyboard { + Keyboard { + keyboard: [ButtonState::Idle; MAX_KEYS], + } + /* + Keyboard { + keyboard: vec![ButtonState::Idle; 256].into_boxed_slice(), + } + */ + } - /// Returns true if the given key was just pressed or is being held down. - #[inline] - pub fn is_key_down(&self, scancode: Scancode) -> bool { - matches!( + /// Returns true if the given key was just pressed or is being held down. + #[inline] + pub fn is_key_down(&self, scancode: Scancode) -> bool { + matches!( self.keyboard[scancode as usize], ButtonState::Pressed | ButtonState::Held ) - } + } - /// Returns true if the given key was not just pressed and is not being held down. - #[inline] - pub fn is_key_up(&self, scancode: Scancode) -> bool { - matches!( + /// Returns true if the given key was not just pressed and is not being held down. + #[inline] + pub fn is_key_up(&self, scancode: Scancode) -> bool { + matches!( self.keyboard[scancode as usize], ButtonState::Released | ButtonState::Idle ) - } + } - /// Returns true if the given key was just pressed (not being held down, yet). - #[inline] - pub fn is_key_pressed(&self, scancode: Scancode) -> bool { - self.keyboard[scancode as usize] == ButtonState::Pressed - } + /// Returns true if the given key was just pressed (not being held down, yet). + #[inline] + pub fn is_key_pressed(&self, scancode: Scancode) -> bool { + self.keyboard[scancode as usize] == ButtonState::Pressed + } - /// Returns true if the given key was just released. - #[inline] - pub fn is_key_released(&self, scancode: Scancode) -> bool { - self.keyboard[scancode as usize] == ButtonState::Released - } + /// Returns true if the given key was just released. + #[inline] + pub fn is_key_released(&self, scancode: Scancode) -> bool { + self.keyboard[scancode as usize] == ButtonState::Released + } } impl InputDevice for Keyboard { - fn update(&mut self) { - for state in self.keyboard.iter_mut() { - *state = match *state { - ButtonState::Pressed => ButtonState::Held, - ButtonState::Released => ButtonState::Idle, - otherwise => otherwise, - }; - } - } + fn update(&mut self) { + for state in self.keyboard.iter_mut() { + *state = match *state { + ButtonState::Pressed => ButtonState::Held, + ButtonState::Released => ButtonState::Idle, + otherwise => otherwise, + }; + } + } } impl SystemEventHandler for Keyboard { - fn handle_event(&mut self, event: &SystemEvent) -> bool { - match event { - SystemEvent::Keyboard(KeyboardEvent::KeyDown { scancode, .. }) => { - if let Some(scancode) = scancode { - let state = &mut self.keyboard[*scancode as usize]; - *state = match *state { - ButtonState::Pressed => ButtonState::Held, - ButtonState::Held => ButtonState::Held, - _ => ButtonState::Pressed, - }; - } - true - } - SystemEvent::Keyboard(KeyboardEvent::KeyUp { scancode, .. }) => { - if let Some(scancode) = scancode { - self.keyboard[*scancode as usize] = ButtonState::Released; - } - true - } - _ => false, - } - } + fn handle_event(&mut self, event: &SystemEvent) -> bool { + match event { + SystemEvent::Keyboard(KeyboardEvent::KeyDown { scancode, .. }) => { + if let Some(scancode) = scancode { + let state = &mut self.keyboard[*scancode as usize]; + *state = match *state { + ButtonState::Pressed => ButtonState::Held, + ButtonState::Held => ButtonState::Held, + _ => ButtonState::Pressed, + }; + } + true + } + SystemEvent::Keyboard(KeyboardEvent::KeyUp { scancode, .. }) => { + if let Some(scancode) = scancode { + self.keyboard[*scancode as usize] = ButtonState::Released; + } + true + } + _ => false, + } + } } \ No newline at end of file diff --git a/libretrogd/src/system/input_devices/keyboard/scancodes.rs b/libretrogd/src/system/input_devices/keyboard/scancodes.rs index 7ef9b6f..ae8b2b0 100644 --- a/libretrogd/src/system/input_devices/keyboard/scancodes.rs +++ b/libretrogd/src/system/input_devices/keyboard/scancodes.rs @@ -3,493 +3,493 @@ #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] #[repr(i32)] pub enum Scancode { - A = sdl2::keyboard::Scancode::A as i32, - B = sdl2::keyboard::Scancode::B as i32, - C = sdl2::keyboard::Scancode::C as i32, - D = sdl2::keyboard::Scancode::D as i32, - E = sdl2::keyboard::Scancode::E as i32, - F = sdl2::keyboard::Scancode::F as i32, - G = sdl2::keyboard::Scancode::G as i32, - H = sdl2::keyboard::Scancode::H as i32, - I = sdl2::keyboard::Scancode::I as i32, - J = sdl2::keyboard::Scancode::J as i32, - K = sdl2::keyboard::Scancode::K as i32, - L = sdl2::keyboard::Scancode::L as i32, - M = sdl2::keyboard::Scancode::M as i32, - N = sdl2::keyboard::Scancode::N as i32, - O = sdl2::keyboard::Scancode::O as i32, - P = sdl2::keyboard::Scancode::P as i32, - Q = sdl2::keyboard::Scancode::Q as i32, - R = sdl2::keyboard::Scancode::R as i32, - S = sdl2::keyboard::Scancode::S as i32, - T = sdl2::keyboard::Scancode::T as i32, - U = sdl2::keyboard::Scancode::U as i32, - V = sdl2::keyboard::Scancode::V as i32, - W = sdl2::keyboard::Scancode::W as i32, - X = sdl2::keyboard::Scancode::X as i32, - Y = sdl2::keyboard::Scancode::Y as i32, - Z = sdl2::keyboard::Scancode::Z as i32, - Num1 = sdl2::keyboard::Scancode::Num1 as i32, - Num2 = sdl2::keyboard::Scancode::Num2 as i32, - Num3 = sdl2::keyboard::Scancode::Num3 as i32, - Num4 = sdl2::keyboard::Scancode::Num4 as i32, - Num5 = sdl2::keyboard::Scancode::Num5 as i32, - Num6 = sdl2::keyboard::Scancode::Num6 as i32, - Num7 = sdl2::keyboard::Scancode::Num7 as i32, - Num8 = sdl2::keyboard::Scancode::Num8 as i32, - Num9 = sdl2::keyboard::Scancode::Num9 as i32, - Num0 = sdl2::keyboard::Scancode::Num0 as i32, - Return = sdl2::keyboard::Scancode::Return as i32, - Escape = sdl2::keyboard::Scancode::Escape as i32, - Backspace = sdl2::keyboard::Scancode::Backspace as i32, - Tab = sdl2::keyboard::Scancode::Tab as i32, - Space = sdl2::keyboard::Scancode::Space as i32, - Minus = sdl2::keyboard::Scancode::Minus as i32, - Equals = sdl2::keyboard::Scancode::Equals as i32, - LeftBracket = sdl2::keyboard::Scancode::LeftBracket as i32, - RightBracket = sdl2::keyboard::Scancode::RightBracket as i32, - Backslash = sdl2::keyboard::Scancode::Backslash as i32, - NonUsHash = sdl2::keyboard::Scancode::NonUsHash as i32, - Semicolon = sdl2::keyboard::Scancode::Semicolon as i32, - Apostrophe = sdl2::keyboard::Scancode::Apostrophe as i32, - Grave = sdl2::keyboard::Scancode::Grave as i32, - Comma = sdl2::keyboard::Scancode::Comma as i32, - Period = sdl2::keyboard::Scancode::Period as i32, - Slash = sdl2::keyboard::Scancode::Slash as i32, - CapsLock = sdl2::keyboard::Scancode::CapsLock as i32, - F1 = sdl2::keyboard::Scancode::F1 as i32, - F2 = sdl2::keyboard::Scancode::F2 as i32, - F3 = sdl2::keyboard::Scancode::F3 as i32, - F4 = sdl2::keyboard::Scancode::F4 as i32, - F5 = sdl2::keyboard::Scancode::F5 as i32, - F6 = sdl2::keyboard::Scancode::F6 as i32, - F7 = sdl2::keyboard::Scancode::F7 as i32, - F8 = sdl2::keyboard::Scancode::F8 as i32, - F9 = sdl2::keyboard::Scancode::F9 as i32, - F10 = sdl2::keyboard::Scancode::F10 as i32, - F11 = sdl2::keyboard::Scancode::F11 as i32, - F12 = sdl2::keyboard::Scancode::F12 as i32, - PrintScreen = sdl2::keyboard::Scancode::PrintScreen as i32, - ScrollLock = sdl2::keyboard::Scancode::ScrollLock as i32, - Pause = sdl2::keyboard::Scancode::Pause as i32, - Insert = sdl2::keyboard::Scancode::Insert as i32, - Home = sdl2::keyboard::Scancode::Home as i32, - PageUp = sdl2::keyboard::Scancode::PageUp as i32, - Delete = sdl2::keyboard::Scancode::Delete as i32, - End = sdl2::keyboard::Scancode::End as i32, - PageDown = sdl2::keyboard::Scancode::PageDown as i32, - Right = sdl2::keyboard::Scancode::Right as i32, - Left = sdl2::keyboard::Scancode::Left as i32, - Down = sdl2::keyboard::Scancode::Down as i32, - Up = sdl2::keyboard::Scancode::Up as i32, - NumLockClear = sdl2::keyboard::Scancode::NumLockClear as i32, - KpDivide = sdl2::keyboard::Scancode::KpDivide as i32, - KpMultiply = sdl2::keyboard::Scancode::KpMultiply as i32, - KpMinus = sdl2::keyboard::Scancode::KpMinus as i32, - KpPlus = sdl2::keyboard::Scancode::KpPlus as i32, - KpEnter = sdl2::keyboard::Scancode::KpEnter as i32, - Kp1 = sdl2::keyboard::Scancode::Kp1 as i32, - Kp2 = sdl2::keyboard::Scancode::Kp2 as i32, - Kp3 = sdl2::keyboard::Scancode::Kp3 as i32, - Kp4 = sdl2::keyboard::Scancode::Kp4 as i32, - Kp5 = sdl2::keyboard::Scancode::Kp5 as i32, - Kp6 = sdl2::keyboard::Scancode::Kp6 as i32, - Kp7 = sdl2::keyboard::Scancode::Kp7 as i32, - Kp8 = sdl2::keyboard::Scancode::Kp8 as i32, - Kp9 = sdl2::keyboard::Scancode::Kp9 as i32, - Kp0 = sdl2::keyboard::Scancode::Kp0 as i32, - KpPeriod = sdl2::keyboard::Scancode::KpPeriod as i32, - NonUsBackslash = sdl2::keyboard::Scancode::NonUsBackslash as i32, - Application = sdl2::keyboard::Scancode::Application as i32, - Power = sdl2::keyboard::Scancode::Power as i32, - KpEquals = sdl2::keyboard::Scancode::KpEquals as i32, - F13 = sdl2::keyboard::Scancode::F13 as i32, - F14 = sdl2::keyboard::Scancode::F14 as i32, - F15 = sdl2::keyboard::Scancode::F15 as i32, - F16 = sdl2::keyboard::Scancode::F16 as i32, - F17 = sdl2::keyboard::Scancode::F17 as i32, - F18 = sdl2::keyboard::Scancode::F18 as i32, - F19 = sdl2::keyboard::Scancode::F19 as i32, - F20 = sdl2::keyboard::Scancode::F20 as i32, - F21 = sdl2::keyboard::Scancode::F21 as i32, - F22 = sdl2::keyboard::Scancode::F22 as i32, - F23 = sdl2::keyboard::Scancode::F23 as i32, - F24 = sdl2::keyboard::Scancode::F24 as i32, - Execute = sdl2::keyboard::Scancode::Execute as i32, - Help = sdl2::keyboard::Scancode::Help as i32, - Menu = sdl2::keyboard::Scancode::Menu as i32, - Select = sdl2::keyboard::Scancode::Select as i32, - Stop = sdl2::keyboard::Scancode::Stop as i32, - Again = sdl2::keyboard::Scancode::Again as i32, - Undo = sdl2::keyboard::Scancode::Undo as i32, - Cut = sdl2::keyboard::Scancode::Cut as i32, - Copy = sdl2::keyboard::Scancode::Copy as i32, - Paste = sdl2::keyboard::Scancode::Paste as i32, - Find = sdl2::keyboard::Scancode::Find as i32, - Mute = sdl2::keyboard::Scancode::Mute as i32, - VolumeUp = sdl2::keyboard::Scancode::VolumeUp as i32, - VolumeDown = sdl2::keyboard::Scancode::VolumeDown as i32, - KpComma = sdl2::keyboard::Scancode::KpComma as i32, - KpEqualsAS400 = sdl2::keyboard::Scancode::KpEqualsAS400 as i32, - International1 = sdl2::keyboard::Scancode::International1 as i32, - International2 = sdl2::keyboard::Scancode::International2 as i32, - International3 = sdl2::keyboard::Scancode::International3 as i32, - International4 = sdl2::keyboard::Scancode::International4 as i32, - International5 = sdl2::keyboard::Scancode::International5 as i32, - International6 = sdl2::keyboard::Scancode::International6 as i32, - International7 = sdl2::keyboard::Scancode::International7 as i32, - International8 = sdl2::keyboard::Scancode::International8 as i32, - International9 = sdl2::keyboard::Scancode::International9 as i32, - Lang1 = sdl2::keyboard::Scancode::Lang1 as i32, - Lang2 = sdl2::keyboard::Scancode::Lang2 as i32, - Lang3 = sdl2::keyboard::Scancode::Lang3 as i32, - Lang4 = sdl2::keyboard::Scancode::Lang4 as i32, - Lang5 = sdl2::keyboard::Scancode::Lang5 as i32, - Lang6 = sdl2::keyboard::Scancode::Lang6 as i32, - Lang7 = sdl2::keyboard::Scancode::Lang7 as i32, - Lang8 = sdl2::keyboard::Scancode::Lang8 as i32, - Lang9 = sdl2::keyboard::Scancode::Lang9 as i32, - AltErase = sdl2::keyboard::Scancode::AltErase as i32, - SysReq = sdl2::keyboard::Scancode::SysReq as i32, - Cancel = sdl2::keyboard::Scancode::Cancel as i32, - Clear = sdl2::keyboard::Scancode::Clear as i32, - Prior = sdl2::keyboard::Scancode::Prior as i32, - Return2 = sdl2::keyboard::Scancode::Return2 as i32, - Separator = sdl2::keyboard::Scancode::Separator as i32, - Out = sdl2::keyboard::Scancode::Out as i32, - Oper = sdl2::keyboard::Scancode::Oper as i32, - ClearAgain = sdl2::keyboard::Scancode::ClearAgain as i32, - CrSel = sdl2::keyboard::Scancode::CrSel as i32, - ExSel = sdl2::keyboard::Scancode::ExSel as i32, - Kp00 = sdl2::keyboard::Scancode::Kp00 as i32, - Kp000 = sdl2::keyboard::Scancode::Kp000 as i32, - ThousandsSeparator = sdl2::keyboard::Scancode::ThousandsSeparator as i32, - DecimalSeparator = sdl2::keyboard::Scancode::DecimalSeparator as i32, - CurrencyUnit = sdl2::keyboard::Scancode::CurrencyUnit as i32, - CurrencySubUnit = sdl2::keyboard::Scancode::CurrencySubUnit as i32, - KpLeftParen = sdl2::keyboard::Scancode::KpLeftParen as i32, - KpRightParen = sdl2::keyboard::Scancode::KpRightParen as i32, - KpLeftBrace = sdl2::keyboard::Scancode::KpLeftBrace as i32, - KpRightBrace = sdl2::keyboard::Scancode::KpRightBrace as i32, - KpTab = sdl2::keyboard::Scancode::KpTab as i32, - KpBackspace = sdl2::keyboard::Scancode::KpBackspace as i32, - KpA = sdl2::keyboard::Scancode::KpA as i32, - KpB = sdl2::keyboard::Scancode::KpB as i32, - KpC = sdl2::keyboard::Scancode::KpC as i32, - KpD = sdl2::keyboard::Scancode::KpD as i32, - KpE = sdl2::keyboard::Scancode::KpE as i32, - KpF = sdl2::keyboard::Scancode::KpF as i32, - KpXor = sdl2::keyboard::Scancode::KpXor as i32, - KpPower = sdl2::keyboard::Scancode::KpPower as i32, - KpPercent = sdl2::keyboard::Scancode::KpPercent as i32, - KpLess = sdl2::keyboard::Scancode::KpLess as i32, - KpGreater = sdl2::keyboard::Scancode::KpGreater as i32, - KpAmpersand = sdl2::keyboard::Scancode::KpAmpersand as i32, - KpDblAmpersand = sdl2::keyboard::Scancode::KpDblAmpersand as i32, - KpVerticalBar = sdl2::keyboard::Scancode::KpVerticalBar as i32, - KpDblVerticalBar = sdl2::keyboard::Scancode::KpDblVerticalBar as i32, - KpColon = sdl2::keyboard::Scancode::KpColon as i32, - KpHash = sdl2::keyboard::Scancode::KpHash as i32, - KpSpace = sdl2::keyboard::Scancode::KpSpace as i32, - KpAt = sdl2::keyboard::Scancode::KpAt as i32, - KpExclam = sdl2::keyboard::Scancode::KpExclam as i32, - KpMemStore = sdl2::keyboard::Scancode::KpMemStore as i32, - KpMemRecall = sdl2::keyboard::Scancode::KpMemRecall as i32, - KpMemClear = sdl2::keyboard::Scancode::KpMemClear as i32, - KpMemAdd = sdl2::keyboard::Scancode::KpMemAdd as i32, - KpMemSubtract = sdl2::keyboard::Scancode::KpMemSubtract as i32, - KpMemMultiply = sdl2::keyboard::Scancode::KpMemMultiply as i32, - KpMemDivide = sdl2::keyboard::Scancode::KpMemDivide as i32, - KpPlusMinus = sdl2::keyboard::Scancode::KpPlusMinus as i32, - KpClear = sdl2::keyboard::Scancode::KpClear as i32, - KpClearEntry = sdl2::keyboard::Scancode::KpClearEntry as i32, - KpBinary = sdl2::keyboard::Scancode::KpBinary as i32, - KpOctal = sdl2::keyboard::Scancode::KpOctal as i32, - KpDecimal = sdl2::keyboard::Scancode::KpDecimal as i32, - KpHexadecimal = sdl2::keyboard::Scancode::KpHexadecimal as i32, - LCtrl = sdl2::keyboard::Scancode::LCtrl as i32, - LShift = sdl2::keyboard::Scancode::LShift as i32, - LAlt = sdl2::keyboard::Scancode::LAlt as i32, - LGui = sdl2::keyboard::Scancode::LGui as i32, - RCtrl = sdl2::keyboard::Scancode::RCtrl as i32, - RShift = sdl2::keyboard::Scancode::RShift as i32, - RAlt = sdl2::keyboard::Scancode::RAlt as i32, - RGui = sdl2::keyboard::Scancode::RGui as i32, - Mode = sdl2::keyboard::Scancode::Mode as i32, - AudioNext = sdl2::keyboard::Scancode::AudioNext as i32, - AudioPrev = sdl2::keyboard::Scancode::AudioPrev as i32, - AudioStop = sdl2::keyboard::Scancode::AudioStop as i32, - AudioPlay = sdl2::keyboard::Scancode::AudioPlay as i32, - AudioMute = sdl2::keyboard::Scancode::AudioMute as i32, - MediaSelect = sdl2::keyboard::Scancode::MediaSelect as i32, - Www = sdl2::keyboard::Scancode::Www as i32, - Mail = sdl2::keyboard::Scancode::Mail as i32, - Calculator = sdl2::keyboard::Scancode::Calculator as i32, - Computer = sdl2::keyboard::Scancode::Computer as i32, - AcSearch = sdl2::keyboard::Scancode::AcSearch as i32, - AcHome = sdl2::keyboard::Scancode::AcHome as i32, - AcBack = sdl2::keyboard::Scancode::AcBack as i32, - AcForward = sdl2::keyboard::Scancode::AcForward as i32, - AcStop = sdl2::keyboard::Scancode::AcStop as i32, - AcRefresh = sdl2::keyboard::Scancode::AcRefresh as i32, - AcBookmarks = sdl2::keyboard::Scancode::AcBookmarks as i32, - BrightnessDown = sdl2::keyboard::Scancode::BrightnessDown as i32, - BrightnessUp = sdl2::keyboard::Scancode::BrightnessUp as i32, - DisplaySwitch = sdl2::keyboard::Scancode::DisplaySwitch as i32, - KbdIllumToggle = sdl2::keyboard::Scancode::KbdIllumToggle as i32, - KbdIllumDown = sdl2::keyboard::Scancode::KbdIllumDown as i32, - KbdIllumUp = sdl2::keyboard::Scancode::KbdIllumUp as i32, - Eject = sdl2::keyboard::Scancode::Eject as i32, - Sleep = sdl2::keyboard::Scancode::Sleep as i32, - App1 = sdl2::keyboard::Scancode::App1 as i32, - App2 = sdl2::keyboard::Scancode::App2 as i32, - Num = sdl2::keyboard::Scancode::Num as i32, + A = sdl2::keyboard::Scancode::A as i32, + B = sdl2::keyboard::Scancode::B as i32, + C = sdl2::keyboard::Scancode::C as i32, + D = sdl2::keyboard::Scancode::D as i32, + E = sdl2::keyboard::Scancode::E as i32, + F = sdl2::keyboard::Scancode::F as i32, + G = sdl2::keyboard::Scancode::G as i32, + H = sdl2::keyboard::Scancode::H as i32, + I = sdl2::keyboard::Scancode::I as i32, + J = sdl2::keyboard::Scancode::J as i32, + K = sdl2::keyboard::Scancode::K as i32, + L = sdl2::keyboard::Scancode::L as i32, + M = sdl2::keyboard::Scancode::M as i32, + N = sdl2::keyboard::Scancode::N as i32, + O = sdl2::keyboard::Scancode::O as i32, + P = sdl2::keyboard::Scancode::P as i32, + Q = sdl2::keyboard::Scancode::Q as i32, + R = sdl2::keyboard::Scancode::R as i32, + S = sdl2::keyboard::Scancode::S as i32, + T = sdl2::keyboard::Scancode::T as i32, + U = sdl2::keyboard::Scancode::U as i32, + V = sdl2::keyboard::Scancode::V as i32, + W = sdl2::keyboard::Scancode::W as i32, + X = sdl2::keyboard::Scancode::X as i32, + Y = sdl2::keyboard::Scancode::Y as i32, + Z = sdl2::keyboard::Scancode::Z as i32, + Num1 = sdl2::keyboard::Scancode::Num1 as i32, + Num2 = sdl2::keyboard::Scancode::Num2 as i32, + Num3 = sdl2::keyboard::Scancode::Num3 as i32, + Num4 = sdl2::keyboard::Scancode::Num4 as i32, + Num5 = sdl2::keyboard::Scancode::Num5 as i32, + Num6 = sdl2::keyboard::Scancode::Num6 as i32, + Num7 = sdl2::keyboard::Scancode::Num7 as i32, + Num8 = sdl2::keyboard::Scancode::Num8 as i32, + Num9 = sdl2::keyboard::Scancode::Num9 as i32, + Num0 = sdl2::keyboard::Scancode::Num0 as i32, + Return = sdl2::keyboard::Scancode::Return as i32, + Escape = sdl2::keyboard::Scancode::Escape as i32, + Backspace = sdl2::keyboard::Scancode::Backspace as i32, + Tab = sdl2::keyboard::Scancode::Tab as i32, + Space = sdl2::keyboard::Scancode::Space as i32, + Minus = sdl2::keyboard::Scancode::Minus as i32, + Equals = sdl2::keyboard::Scancode::Equals as i32, + LeftBracket = sdl2::keyboard::Scancode::LeftBracket as i32, + RightBracket = sdl2::keyboard::Scancode::RightBracket as i32, + Backslash = sdl2::keyboard::Scancode::Backslash as i32, + NonUsHash = sdl2::keyboard::Scancode::NonUsHash as i32, + Semicolon = sdl2::keyboard::Scancode::Semicolon as i32, + Apostrophe = sdl2::keyboard::Scancode::Apostrophe as i32, + Grave = sdl2::keyboard::Scancode::Grave as i32, + Comma = sdl2::keyboard::Scancode::Comma as i32, + Period = sdl2::keyboard::Scancode::Period as i32, + Slash = sdl2::keyboard::Scancode::Slash as i32, + CapsLock = sdl2::keyboard::Scancode::CapsLock as i32, + F1 = sdl2::keyboard::Scancode::F1 as i32, + F2 = sdl2::keyboard::Scancode::F2 as i32, + F3 = sdl2::keyboard::Scancode::F3 as i32, + F4 = sdl2::keyboard::Scancode::F4 as i32, + F5 = sdl2::keyboard::Scancode::F5 as i32, + F6 = sdl2::keyboard::Scancode::F6 as i32, + F7 = sdl2::keyboard::Scancode::F7 as i32, + F8 = sdl2::keyboard::Scancode::F8 as i32, + F9 = sdl2::keyboard::Scancode::F9 as i32, + F10 = sdl2::keyboard::Scancode::F10 as i32, + F11 = sdl2::keyboard::Scancode::F11 as i32, + F12 = sdl2::keyboard::Scancode::F12 as i32, + PrintScreen = sdl2::keyboard::Scancode::PrintScreen as i32, + ScrollLock = sdl2::keyboard::Scancode::ScrollLock as i32, + Pause = sdl2::keyboard::Scancode::Pause as i32, + Insert = sdl2::keyboard::Scancode::Insert as i32, + Home = sdl2::keyboard::Scancode::Home as i32, + PageUp = sdl2::keyboard::Scancode::PageUp as i32, + Delete = sdl2::keyboard::Scancode::Delete as i32, + End = sdl2::keyboard::Scancode::End as i32, + PageDown = sdl2::keyboard::Scancode::PageDown as i32, + Right = sdl2::keyboard::Scancode::Right as i32, + Left = sdl2::keyboard::Scancode::Left as i32, + Down = sdl2::keyboard::Scancode::Down as i32, + Up = sdl2::keyboard::Scancode::Up as i32, + NumLockClear = sdl2::keyboard::Scancode::NumLockClear as i32, + KpDivide = sdl2::keyboard::Scancode::KpDivide as i32, + KpMultiply = sdl2::keyboard::Scancode::KpMultiply as i32, + KpMinus = sdl2::keyboard::Scancode::KpMinus as i32, + KpPlus = sdl2::keyboard::Scancode::KpPlus as i32, + KpEnter = sdl2::keyboard::Scancode::KpEnter as i32, + Kp1 = sdl2::keyboard::Scancode::Kp1 as i32, + Kp2 = sdl2::keyboard::Scancode::Kp2 as i32, + Kp3 = sdl2::keyboard::Scancode::Kp3 as i32, + Kp4 = sdl2::keyboard::Scancode::Kp4 as i32, + Kp5 = sdl2::keyboard::Scancode::Kp5 as i32, + Kp6 = sdl2::keyboard::Scancode::Kp6 as i32, + Kp7 = sdl2::keyboard::Scancode::Kp7 as i32, + Kp8 = sdl2::keyboard::Scancode::Kp8 as i32, + Kp9 = sdl2::keyboard::Scancode::Kp9 as i32, + Kp0 = sdl2::keyboard::Scancode::Kp0 as i32, + KpPeriod = sdl2::keyboard::Scancode::KpPeriod as i32, + NonUsBackslash = sdl2::keyboard::Scancode::NonUsBackslash as i32, + Application = sdl2::keyboard::Scancode::Application as i32, + Power = sdl2::keyboard::Scancode::Power as i32, + KpEquals = sdl2::keyboard::Scancode::KpEquals as i32, + F13 = sdl2::keyboard::Scancode::F13 as i32, + F14 = sdl2::keyboard::Scancode::F14 as i32, + F15 = sdl2::keyboard::Scancode::F15 as i32, + F16 = sdl2::keyboard::Scancode::F16 as i32, + F17 = sdl2::keyboard::Scancode::F17 as i32, + F18 = sdl2::keyboard::Scancode::F18 as i32, + F19 = sdl2::keyboard::Scancode::F19 as i32, + F20 = sdl2::keyboard::Scancode::F20 as i32, + F21 = sdl2::keyboard::Scancode::F21 as i32, + F22 = sdl2::keyboard::Scancode::F22 as i32, + F23 = sdl2::keyboard::Scancode::F23 as i32, + F24 = sdl2::keyboard::Scancode::F24 as i32, + Execute = sdl2::keyboard::Scancode::Execute as i32, + Help = sdl2::keyboard::Scancode::Help as i32, + Menu = sdl2::keyboard::Scancode::Menu as i32, + Select = sdl2::keyboard::Scancode::Select as i32, + Stop = sdl2::keyboard::Scancode::Stop as i32, + Again = sdl2::keyboard::Scancode::Again as i32, + Undo = sdl2::keyboard::Scancode::Undo as i32, + Cut = sdl2::keyboard::Scancode::Cut as i32, + Copy = sdl2::keyboard::Scancode::Copy as i32, + Paste = sdl2::keyboard::Scancode::Paste as i32, + Find = sdl2::keyboard::Scancode::Find as i32, + Mute = sdl2::keyboard::Scancode::Mute as i32, + VolumeUp = sdl2::keyboard::Scancode::VolumeUp as i32, + VolumeDown = sdl2::keyboard::Scancode::VolumeDown as i32, + KpComma = sdl2::keyboard::Scancode::KpComma as i32, + KpEqualsAS400 = sdl2::keyboard::Scancode::KpEqualsAS400 as i32, + International1 = sdl2::keyboard::Scancode::International1 as i32, + International2 = sdl2::keyboard::Scancode::International2 as i32, + International3 = sdl2::keyboard::Scancode::International3 as i32, + International4 = sdl2::keyboard::Scancode::International4 as i32, + International5 = sdl2::keyboard::Scancode::International5 as i32, + International6 = sdl2::keyboard::Scancode::International6 as i32, + International7 = sdl2::keyboard::Scancode::International7 as i32, + International8 = sdl2::keyboard::Scancode::International8 as i32, + International9 = sdl2::keyboard::Scancode::International9 as i32, + Lang1 = sdl2::keyboard::Scancode::Lang1 as i32, + Lang2 = sdl2::keyboard::Scancode::Lang2 as i32, + Lang3 = sdl2::keyboard::Scancode::Lang3 as i32, + Lang4 = sdl2::keyboard::Scancode::Lang4 as i32, + Lang5 = sdl2::keyboard::Scancode::Lang5 as i32, + Lang6 = sdl2::keyboard::Scancode::Lang6 as i32, + Lang7 = sdl2::keyboard::Scancode::Lang7 as i32, + Lang8 = sdl2::keyboard::Scancode::Lang8 as i32, + Lang9 = sdl2::keyboard::Scancode::Lang9 as i32, + AltErase = sdl2::keyboard::Scancode::AltErase as i32, + SysReq = sdl2::keyboard::Scancode::SysReq as i32, + Cancel = sdl2::keyboard::Scancode::Cancel as i32, + Clear = sdl2::keyboard::Scancode::Clear as i32, + Prior = sdl2::keyboard::Scancode::Prior as i32, + Return2 = sdl2::keyboard::Scancode::Return2 as i32, + Separator = sdl2::keyboard::Scancode::Separator as i32, + Out = sdl2::keyboard::Scancode::Out as i32, + Oper = sdl2::keyboard::Scancode::Oper as i32, + ClearAgain = sdl2::keyboard::Scancode::ClearAgain as i32, + CrSel = sdl2::keyboard::Scancode::CrSel as i32, + ExSel = sdl2::keyboard::Scancode::ExSel as i32, + Kp00 = sdl2::keyboard::Scancode::Kp00 as i32, + Kp000 = sdl2::keyboard::Scancode::Kp000 as i32, + ThousandsSeparator = sdl2::keyboard::Scancode::ThousandsSeparator as i32, + DecimalSeparator = sdl2::keyboard::Scancode::DecimalSeparator as i32, + CurrencyUnit = sdl2::keyboard::Scancode::CurrencyUnit as i32, + CurrencySubUnit = sdl2::keyboard::Scancode::CurrencySubUnit as i32, + KpLeftParen = sdl2::keyboard::Scancode::KpLeftParen as i32, + KpRightParen = sdl2::keyboard::Scancode::KpRightParen as i32, + KpLeftBrace = sdl2::keyboard::Scancode::KpLeftBrace as i32, + KpRightBrace = sdl2::keyboard::Scancode::KpRightBrace as i32, + KpTab = sdl2::keyboard::Scancode::KpTab as i32, + KpBackspace = sdl2::keyboard::Scancode::KpBackspace as i32, + KpA = sdl2::keyboard::Scancode::KpA as i32, + KpB = sdl2::keyboard::Scancode::KpB as i32, + KpC = sdl2::keyboard::Scancode::KpC as i32, + KpD = sdl2::keyboard::Scancode::KpD as i32, + KpE = sdl2::keyboard::Scancode::KpE as i32, + KpF = sdl2::keyboard::Scancode::KpF as i32, + KpXor = sdl2::keyboard::Scancode::KpXor as i32, + KpPower = sdl2::keyboard::Scancode::KpPower as i32, + KpPercent = sdl2::keyboard::Scancode::KpPercent as i32, + KpLess = sdl2::keyboard::Scancode::KpLess as i32, + KpGreater = sdl2::keyboard::Scancode::KpGreater as i32, + KpAmpersand = sdl2::keyboard::Scancode::KpAmpersand as i32, + KpDblAmpersand = sdl2::keyboard::Scancode::KpDblAmpersand as i32, + KpVerticalBar = sdl2::keyboard::Scancode::KpVerticalBar as i32, + KpDblVerticalBar = sdl2::keyboard::Scancode::KpDblVerticalBar as i32, + KpColon = sdl2::keyboard::Scancode::KpColon as i32, + KpHash = sdl2::keyboard::Scancode::KpHash as i32, + KpSpace = sdl2::keyboard::Scancode::KpSpace as i32, + KpAt = sdl2::keyboard::Scancode::KpAt as i32, + KpExclam = sdl2::keyboard::Scancode::KpExclam as i32, + KpMemStore = sdl2::keyboard::Scancode::KpMemStore as i32, + KpMemRecall = sdl2::keyboard::Scancode::KpMemRecall as i32, + KpMemClear = sdl2::keyboard::Scancode::KpMemClear as i32, + KpMemAdd = sdl2::keyboard::Scancode::KpMemAdd as i32, + KpMemSubtract = sdl2::keyboard::Scancode::KpMemSubtract as i32, + KpMemMultiply = sdl2::keyboard::Scancode::KpMemMultiply as i32, + KpMemDivide = sdl2::keyboard::Scancode::KpMemDivide as i32, + KpPlusMinus = sdl2::keyboard::Scancode::KpPlusMinus as i32, + KpClear = sdl2::keyboard::Scancode::KpClear as i32, + KpClearEntry = sdl2::keyboard::Scancode::KpClearEntry as i32, + KpBinary = sdl2::keyboard::Scancode::KpBinary as i32, + KpOctal = sdl2::keyboard::Scancode::KpOctal as i32, + KpDecimal = sdl2::keyboard::Scancode::KpDecimal as i32, + KpHexadecimal = sdl2::keyboard::Scancode::KpHexadecimal as i32, + LCtrl = sdl2::keyboard::Scancode::LCtrl as i32, + LShift = sdl2::keyboard::Scancode::LShift as i32, + LAlt = sdl2::keyboard::Scancode::LAlt as i32, + LGui = sdl2::keyboard::Scancode::LGui as i32, + RCtrl = sdl2::keyboard::Scancode::RCtrl as i32, + RShift = sdl2::keyboard::Scancode::RShift as i32, + RAlt = sdl2::keyboard::Scancode::RAlt as i32, + RGui = sdl2::keyboard::Scancode::RGui as i32, + Mode = sdl2::keyboard::Scancode::Mode as i32, + AudioNext = sdl2::keyboard::Scancode::AudioNext as i32, + AudioPrev = sdl2::keyboard::Scancode::AudioPrev as i32, + AudioStop = sdl2::keyboard::Scancode::AudioStop as i32, + AudioPlay = sdl2::keyboard::Scancode::AudioPlay as i32, + AudioMute = sdl2::keyboard::Scancode::AudioMute as i32, + MediaSelect = sdl2::keyboard::Scancode::MediaSelect as i32, + Www = sdl2::keyboard::Scancode::Www as i32, + Mail = sdl2::keyboard::Scancode::Mail as i32, + Calculator = sdl2::keyboard::Scancode::Calculator as i32, + Computer = sdl2::keyboard::Scancode::Computer as i32, + AcSearch = sdl2::keyboard::Scancode::AcSearch as i32, + AcHome = sdl2::keyboard::Scancode::AcHome as i32, + AcBack = sdl2::keyboard::Scancode::AcBack as i32, + AcForward = sdl2::keyboard::Scancode::AcForward as i32, + AcStop = sdl2::keyboard::Scancode::AcStop as i32, + AcRefresh = sdl2::keyboard::Scancode::AcRefresh as i32, + AcBookmarks = sdl2::keyboard::Scancode::AcBookmarks as i32, + BrightnessDown = sdl2::keyboard::Scancode::BrightnessDown as i32, + BrightnessUp = sdl2::keyboard::Scancode::BrightnessUp as i32, + DisplaySwitch = sdl2::keyboard::Scancode::DisplaySwitch as i32, + KbdIllumToggle = sdl2::keyboard::Scancode::KbdIllumToggle as i32, + KbdIllumDown = sdl2::keyboard::Scancode::KbdIllumDown as i32, + KbdIllumUp = sdl2::keyboard::Scancode::KbdIllumUp as i32, + Eject = sdl2::keyboard::Scancode::Eject as i32, + Sleep = sdl2::keyboard::Scancode::Sleep as i32, + App1 = sdl2::keyboard::Scancode::App1 as i32, + App2 = sdl2::keyboard::Scancode::App2 as i32, + Num = sdl2::keyboard::Scancode::Num as i32, } impl From for Scancode { - fn from(value: sdl2::keyboard::Scancode) -> Self { - match value { - sdl2::keyboard::Scancode::A => Scancode::A, - sdl2::keyboard::Scancode::B => Scancode::B, - sdl2::keyboard::Scancode::C => Scancode::C, - sdl2::keyboard::Scancode::D => Scancode::D, - sdl2::keyboard::Scancode::E => Scancode::E, - sdl2::keyboard::Scancode::F => Scancode::F, - sdl2::keyboard::Scancode::G => Scancode::G, - sdl2::keyboard::Scancode::H => Scancode::H, - sdl2::keyboard::Scancode::I => Scancode::I, - sdl2::keyboard::Scancode::J => Scancode::J, - sdl2::keyboard::Scancode::K => Scancode::K, - sdl2::keyboard::Scancode::L => Scancode::L, - sdl2::keyboard::Scancode::M => Scancode::M, - sdl2::keyboard::Scancode::N => Scancode::N, - sdl2::keyboard::Scancode::O => Scancode::O, - sdl2::keyboard::Scancode::P => Scancode::P, - sdl2::keyboard::Scancode::Q => Scancode::Q, - sdl2::keyboard::Scancode::R => Scancode::R, - sdl2::keyboard::Scancode::S => Scancode::S, - sdl2::keyboard::Scancode::T => Scancode::T, - sdl2::keyboard::Scancode::U => Scancode::U, - sdl2::keyboard::Scancode::V => Scancode::V, - sdl2::keyboard::Scancode::W => Scancode::W, - sdl2::keyboard::Scancode::X => Scancode::X, - sdl2::keyboard::Scancode::Y => Scancode::Y, - sdl2::keyboard::Scancode::Z => Scancode::Z, - sdl2::keyboard::Scancode::Num1 => Scancode::Num1, - sdl2::keyboard::Scancode::Num2 => Scancode::Num2, - sdl2::keyboard::Scancode::Num3 => Scancode::Num3, - sdl2::keyboard::Scancode::Num4 => Scancode::Num4, - sdl2::keyboard::Scancode::Num5 => Scancode::Num5, - sdl2::keyboard::Scancode::Num6 => Scancode::Num6, - sdl2::keyboard::Scancode::Num7 => Scancode::Num7, - sdl2::keyboard::Scancode::Num8 => Scancode::Num8, - sdl2::keyboard::Scancode::Num9 => Scancode::Num9, - sdl2::keyboard::Scancode::Num0 => Scancode::Num0, - sdl2::keyboard::Scancode::Return => Scancode::Return, - sdl2::keyboard::Scancode::Escape => Scancode::Escape, - sdl2::keyboard::Scancode::Backspace => Scancode::Backspace, - sdl2::keyboard::Scancode::Tab => Scancode::Tab, - sdl2::keyboard::Scancode::Space => Scancode::Space, - sdl2::keyboard::Scancode::Minus => Scancode::Minus, - sdl2::keyboard::Scancode::Equals => Scancode::Equals, - sdl2::keyboard::Scancode::LeftBracket => Scancode::LeftBracket, - sdl2::keyboard::Scancode::RightBracket => Scancode::RightBracket, - sdl2::keyboard::Scancode::Backslash => Scancode::Backslash, - sdl2::keyboard::Scancode::NonUsHash => Scancode::NonUsHash, - sdl2::keyboard::Scancode::Semicolon => Scancode::Semicolon, - sdl2::keyboard::Scancode::Apostrophe => Scancode::Apostrophe, - sdl2::keyboard::Scancode::Grave => Scancode::Grave, - sdl2::keyboard::Scancode::Comma => Scancode::Comma, - sdl2::keyboard::Scancode::Period => Scancode::Period, - sdl2::keyboard::Scancode::Slash => Scancode::Slash, - sdl2::keyboard::Scancode::CapsLock => Scancode::CapsLock, - sdl2::keyboard::Scancode::F1 => Scancode::F1, - sdl2::keyboard::Scancode::F2 => Scancode::F2, - sdl2::keyboard::Scancode::F3 => Scancode::F3, - sdl2::keyboard::Scancode::F4 => Scancode::F4, - sdl2::keyboard::Scancode::F5 => Scancode::F5, - sdl2::keyboard::Scancode::F6 => Scancode::F6, - sdl2::keyboard::Scancode::F7 => Scancode::F7, - sdl2::keyboard::Scancode::F8 => Scancode::F8, - sdl2::keyboard::Scancode::F9 => Scancode::F9, - sdl2::keyboard::Scancode::F10 => Scancode::F10, - sdl2::keyboard::Scancode::F11 => Scancode::F11, - sdl2::keyboard::Scancode::F12 => Scancode::F12, - sdl2::keyboard::Scancode::PrintScreen => Scancode::PrintScreen, - sdl2::keyboard::Scancode::ScrollLock => Scancode::ScrollLock, - sdl2::keyboard::Scancode::Pause => Scancode::Pause, - sdl2::keyboard::Scancode::Insert => Scancode::Insert, - sdl2::keyboard::Scancode::Home => Scancode::Home, - sdl2::keyboard::Scancode::PageUp => Scancode::PageUp, - sdl2::keyboard::Scancode::Delete => Scancode::Delete, - sdl2::keyboard::Scancode::End => Scancode::End, - sdl2::keyboard::Scancode::PageDown => Scancode::PageDown, - sdl2::keyboard::Scancode::Right => Scancode::Right, - sdl2::keyboard::Scancode::Left => Scancode::Left, - sdl2::keyboard::Scancode::Down => Scancode::Down, - sdl2::keyboard::Scancode::Up => Scancode::Up, - sdl2::keyboard::Scancode::NumLockClear => Scancode::NumLockClear, - sdl2::keyboard::Scancode::KpDivide => Scancode::KpDivide, - sdl2::keyboard::Scancode::KpMultiply => Scancode::KpMultiply, - sdl2::keyboard::Scancode::KpMinus => Scancode::KpMinus, - sdl2::keyboard::Scancode::KpPlus => Scancode::KpPlus, - sdl2::keyboard::Scancode::KpEnter => Scancode::KpEnter, - sdl2::keyboard::Scancode::Kp1 => Scancode::Kp1, - sdl2::keyboard::Scancode::Kp2 => Scancode::Kp2, - sdl2::keyboard::Scancode::Kp3 => Scancode::Kp3, - sdl2::keyboard::Scancode::Kp4 => Scancode::Kp4, - sdl2::keyboard::Scancode::Kp5 => Scancode::Kp5, - sdl2::keyboard::Scancode::Kp6 => Scancode::Kp6, - sdl2::keyboard::Scancode::Kp7 => Scancode::Kp7, - sdl2::keyboard::Scancode::Kp8 => Scancode::Kp8, - sdl2::keyboard::Scancode::Kp9 => Scancode::Kp9, - sdl2::keyboard::Scancode::Kp0 => Scancode::Kp0, - sdl2::keyboard::Scancode::KpPeriod => Scancode::KpPeriod, - sdl2::keyboard::Scancode::NonUsBackslash => Scancode::NonUsBackslash, - sdl2::keyboard::Scancode::Application => Scancode::Application, - sdl2::keyboard::Scancode::Power => Scancode::Power, - sdl2::keyboard::Scancode::KpEquals => Scancode::KpEquals, - sdl2::keyboard::Scancode::F13 => Scancode::F13, - sdl2::keyboard::Scancode::F14 => Scancode::F14, - sdl2::keyboard::Scancode::F15 => Scancode::F15, - sdl2::keyboard::Scancode::F16 => Scancode::F16, - sdl2::keyboard::Scancode::F17 => Scancode::F17, - sdl2::keyboard::Scancode::F18 => Scancode::F18, - sdl2::keyboard::Scancode::F19 => Scancode::F19, - sdl2::keyboard::Scancode::F20 => Scancode::F20, - sdl2::keyboard::Scancode::F21 => Scancode::F21, - sdl2::keyboard::Scancode::F22 => Scancode::F22, - sdl2::keyboard::Scancode::F23 => Scancode::F23, - sdl2::keyboard::Scancode::F24 => Scancode::F24, - sdl2::keyboard::Scancode::Execute => Scancode::Execute, - sdl2::keyboard::Scancode::Help => Scancode::Help, - sdl2::keyboard::Scancode::Menu => Scancode::Menu, - sdl2::keyboard::Scancode::Select => Scancode::Select, - sdl2::keyboard::Scancode::Stop => Scancode::Stop, - sdl2::keyboard::Scancode::Again => Scancode::Again, - sdl2::keyboard::Scancode::Undo => Scancode::Undo, - sdl2::keyboard::Scancode::Cut => Scancode::Cut, - sdl2::keyboard::Scancode::Copy => Scancode::Copy, - sdl2::keyboard::Scancode::Paste => Scancode::Paste, - sdl2::keyboard::Scancode::Find => Scancode::Find, - sdl2::keyboard::Scancode::Mute => Scancode::Mute, - sdl2::keyboard::Scancode::VolumeUp => Scancode::VolumeUp, - sdl2::keyboard::Scancode::VolumeDown => Scancode::VolumeDown, - sdl2::keyboard::Scancode::KpComma => Scancode::KpComma, - sdl2::keyboard::Scancode::KpEqualsAS400 => Scancode::KpEqualsAS400, - sdl2::keyboard::Scancode::International1 => Scancode::International1, - sdl2::keyboard::Scancode::International2 => Scancode::International2, - sdl2::keyboard::Scancode::International3 => Scancode::International3, - sdl2::keyboard::Scancode::International4 => Scancode::International4, - sdl2::keyboard::Scancode::International5 => Scancode::International5, - sdl2::keyboard::Scancode::International6 => Scancode::International6, - sdl2::keyboard::Scancode::International7 => Scancode::International7, - sdl2::keyboard::Scancode::International8 => Scancode::International8, - sdl2::keyboard::Scancode::International9 => Scancode::International9, - sdl2::keyboard::Scancode::Lang1 => Scancode::Lang1, - sdl2::keyboard::Scancode::Lang2 => Scancode::Lang2, - sdl2::keyboard::Scancode::Lang3 => Scancode::Lang3, - sdl2::keyboard::Scancode::Lang4 => Scancode::Lang4, - sdl2::keyboard::Scancode::Lang5 => Scancode::Lang5, - sdl2::keyboard::Scancode::Lang6 => Scancode::Lang6, - sdl2::keyboard::Scancode::Lang7 => Scancode::Lang7, - sdl2::keyboard::Scancode::Lang8 => Scancode::Lang8, - sdl2::keyboard::Scancode::Lang9 => Scancode::Lang9, - sdl2::keyboard::Scancode::AltErase => Scancode::AltErase, - sdl2::keyboard::Scancode::SysReq => Scancode::SysReq, - sdl2::keyboard::Scancode::Cancel => Scancode::Cancel, - sdl2::keyboard::Scancode::Clear => Scancode::Clear, - sdl2::keyboard::Scancode::Prior => Scancode::Prior, - sdl2::keyboard::Scancode::Return2 => Scancode::Return2, - sdl2::keyboard::Scancode::Separator => Scancode::Separator, - sdl2::keyboard::Scancode::Out => Scancode::Out, - sdl2::keyboard::Scancode::Oper => Scancode::Oper, - sdl2::keyboard::Scancode::ClearAgain => Scancode::ClearAgain, - sdl2::keyboard::Scancode::CrSel => Scancode::CrSel, - sdl2::keyboard::Scancode::ExSel => Scancode::ExSel, - sdl2::keyboard::Scancode::Kp00 => Scancode::Kp00, - sdl2::keyboard::Scancode::Kp000 => Scancode::Kp000, - sdl2::keyboard::Scancode::ThousandsSeparator => Scancode::ThousandsSeparator, - sdl2::keyboard::Scancode::DecimalSeparator => Scancode::DecimalSeparator, - sdl2::keyboard::Scancode::CurrencyUnit => Scancode::CurrencyUnit, - sdl2::keyboard::Scancode::CurrencySubUnit => Scancode::CurrencySubUnit, - sdl2::keyboard::Scancode::KpLeftParen => Scancode::KpLeftParen, - sdl2::keyboard::Scancode::KpRightParen => Scancode::KpRightParen, - sdl2::keyboard::Scancode::KpLeftBrace => Scancode::KpLeftBrace, - sdl2::keyboard::Scancode::KpRightBrace => Scancode::KpRightBrace, - sdl2::keyboard::Scancode::KpTab => Scancode::KpTab, - sdl2::keyboard::Scancode::KpBackspace => Scancode::KpBackspace, - sdl2::keyboard::Scancode::KpA => Scancode::KpA, - sdl2::keyboard::Scancode::KpB => Scancode::KpB, - sdl2::keyboard::Scancode::KpC => Scancode::KpC, - sdl2::keyboard::Scancode::KpD => Scancode::KpD, - sdl2::keyboard::Scancode::KpE => Scancode::KpE, - sdl2::keyboard::Scancode::KpF => Scancode::KpF, - sdl2::keyboard::Scancode::KpXor => Scancode::KpXor, - sdl2::keyboard::Scancode::KpPower => Scancode::KpPower, - sdl2::keyboard::Scancode::KpPercent => Scancode::KpPercent, - sdl2::keyboard::Scancode::KpLess => Scancode::KpLess, - sdl2::keyboard::Scancode::KpGreater => Scancode::KpGreater, - sdl2::keyboard::Scancode::KpAmpersand => Scancode::KpAmpersand, - sdl2::keyboard::Scancode::KpDblAmpersand => Scancode::KpDblAmpersand, - sdl2::keyboard::Scancode::KpVerticalBar => Scancode::KpVerticalBar, - sdl2::keyboard::Scancode::KpDblVerticalBar => Scancode::KpDblVerticalBar, - sdl2::keyboard::Scancode::KpColon => Scancode::KpColon, - sdl2::keyboard::Scancode::KpHash => Scancode::KpHash, - sdl2::keyboard::Scancode::KpSpace => Scancode::KpSpace, - sdl2::keyboard::Scancode::KpAt => Scancode::KpAt, - sdl2::keyboard::Scancode::KpExclam => Scancode::KpExclam, - sdl2::keyboard::Scancode::KpMemStore => Scancode::KpMemStore, - sdl2::keyboard::Scancode::KpMemRecall => Scancode::KpMemRecall, - sdl2::keyboard::Scancode::KpMemClear => Scancode::KpMemClear, - sdl2::keyboard::Scancode::KpMemAdd => Scancode::KpMemAdd, - sdl2::keyboard::Scancode::KpMemSubtract => Scancode::KpMemSubtract, - sdl2::keyboard::Scancode::KpMemMultiply => Scancode::KpMemMultiply, - sdl2::keyboard::Scancode::KpMemDivide => Scancode::KpMemDivide, - sdl2::keyboard::Scancode::KpPlusMinus => Scancode::KpPlusMinus, - sdl2::keyboard::Scancode::KpClear => Scancode::KpClear, - sdl2::keyboard::Scancode::KpClearEntry => Scancode::KpClearEntry, - sdl2::keyboard::Scancode::KpBinary => Scancode::KpBinary, - sdl2::keyboard::Scancode::KpOctal => Scancode::KpOctal, - sdl2::keyboard::Scancode::KpDecimal => Scancode::KpDecimal, - sdl2::keyboard::Scancode::KpHexadecimal => Scancode::KpHexadecimal, - sdl2::keyboard::Scancode::LCtrl => Scancode::LCtrl, - sdl2::keyboard::Scancode::LShift => Scancode::LShift, - sdl2::keyboard::Scancode::LAlt => Scancode::LAlt, - sdl2::keyboard::Scancode::LGui => Scancode::LGui, - sdl2::keyboard::Scancode::RCtrl => Scancode::RCtrl, - sdl2::keyboard::Scancode::RShift => Scancode::RShift, - sdl2::keyboard::Scancode::RAlt => Scancode::RAlt, - sdl2::keyboard::Scancode::RGui => Scancode::RGui, - sdl2::keyboard::Scancode::Mode => Scancode::Mode, - sdl2::keyboard::Scancode::AudioNext => Scancode::AudioNext, - sdl2::keyboard::Scancode::AudioPrev => Scancode::AudioPrev, - sdl2::keyboard::Scancode::AudioStop => Scancode::AudioStop, - sdl2::keyboard::Scancode::AudioPlay => Scancode::AudioPlay, - sdl2::keyboard::Scancode::AudioMute => Scancode::AudioMute, - sdl2::keyboard::Scancode::MediaSelect => Scancode::MediaSelect, - sdl2::keyboard::Scancode::Www => Scancode::Www, - sdl2::keyboard::Scancode::Mail => Scancode::Mail, - sdl2::keyboard::Scancode::Calculator => Scancode::Calculator, - sdl2::keyboard::Scancode::Computer => Scancode::Computer, - sdl2::keyboard::Scancode::AcSearch => Scancode::AcSearch, - sdl2::keyboard::Scancode::AcHome => Scancode::AcHome, - sdl2::keyboard::Scancode::AcBack => Scancode::AcBack, - sdl2::keyboard::Scancode::AcForward => Scancode::AcForward, - sdl2::keyboard::Scancode::AcStop => Scancode::AcStop, - sdl2::keyboard::Scancode::AcRefresh => Scancode::AcRefresh, - sdl2::keyboard::Scancode::AcBookmarks => Scancode::AcBookmarks, - sdl2::keyboard::Scancode::BrightnessDown => Scancode::BrightnessDown, - sdl2::keyboard::Scancode::BrightnessUp => Scancode::BrightnessUp, - sdl2::keyboard::Scancode::DisplaySwitch => Scancode::DisplaySwitch, - sdl2::keyboard::Scancode::KbdIllumToggle => Scancode::KbdIllumToggle, - sdl2::keyboard::Scancode::KbdIllumDown => Scancode::KbdIllumDown, - sdl2::keyboard::Scancode::KbdIllumUp => Scancode::KbdIllumUp, - sdl2::keyboard::Scancode::Eject => Scancode::Eject, - sdl2::keyboard::Scancode::Sleep => Scancode::Sleep, - sdl2::keyboard::Scancode::App1 => Scancode::App1, - sdl2::keyboard::Scancode::App2 => Scancode::App2, - sdl2::keyboard::Scancode::Num => Scancode::Num, - } - } + fn from(value: sdl2::keyboard::Scancode) -> Self { + match value { + sdl2::keyboard::Scancode::A => Scancode::A, + sdl2::keyboard::Scancode::B => Scancode::B, + sdl2::keyboard::Scancode::C => Scancode::C, + sdl2::keyboard::Scancode::D => Scancode::D, + sdl2::keyboard::Scancode::E => Scancode::E, + sdl2::keyboard::Scancode::F => Scancode::F, + sdl2::keyboard::Scancode::G => Scancode::G, + sdl2::keyboard::Scancode::H => Scancode::H, + sdl2::keyboard::Scancode::I => Scancode::I, + sdl2::keyboard::Scancode::J => Scancode::J, + sdl2::keyboard::Scancode::K => Scancode::K, + sdl2::keyboard::Scancode::L => Scancode::L, + sdl2::keyboard::Scancode::M => Scancode::M, + sdl2::keyboard::Scancode::N => Scancode::N, + sdl2::keyboard::Scancode::O => Scancode::O, + sdl2::keyboard::Scancode::P => Scancode::P, + sdl2::keyboard::Scancode::Q => Scancode::Q, + sdl2::keyboard::Scancode::R => Scancode::R, + sdl2::keyboard::Scancode::S => Scancode::S, + sdl2::keyboard::Scancode::T => Scancode::T, + sdl2::keyboard::Scancode::U => Scancode::U, + sdl2::keyboard::Scancode::V => Scancode::V, + sdl2::keyboard::Scancode::W => Scancode::W, + sdl2::keyboard::Scancode::X => Scancode::X, + sdl2::keyboard::Scancode::Y => Scancode::Y, + sdl2::keyboard::Scancode::Z => Scancode::Z, + sdl2::keyboard::Scancode::Num1 => Scancode::Num1, + sdl2::keyboard::Scancode::Num2 => Scancode::Num2, + sdl2::keyboard::Scancode::Num3 => Scancode::Num3, + sdl2::keyboard::Scancode::Num4 => Scancode::Num4, + sdl2::keyboard::Scancode::Num5 => Scancode::Num5, + sdl2::keyboard::Scancode::Num6 => Scancode::Num6, + sdl2::keyboard::Scancode::Num7 => Scancode::Num7, + sdl2::keyboard::Scancode::Num8 => Scancode::Num8, + sdl2::keyboard::Scancode::Num9 => Scancode::Num9, + sdl2::keyboard::Scancode::Num0 => Scancode::Num0, + sdl2::keyboard::Scancode::Return => Scancode::Return, + sdl2::keyboard::Scancode::Escape => Scancode::Escape, + sdl2::keyboard::Scancode::Backspace => Scancode::Backspace, + sdl2::keyboard::Scancode::Tab => Scancode::Tab, + sdl2::keyboard::Scancode::Space => Scancode::Space, + sdl2::keyboard::Scancode::Minus => Scancode::Minus, + sdl2::keyboard::Scancode::Equals => Scancode::Equals, + sdl2::keyboard::Scancode::LeftBracket => Scancode::LeftBracket, + sdl2::keyboard::Scancode::RightBracket => Scancode::RightBracket, + sdl2::keyboard::Scancode::Backslash => Scancode::Backslash, + sdl2::keyboard::Scancode::NonUsHash => Scancode::NonUsHash, + sdl2::keyboard::Scancode::Semicolon => Scancode::Semicolon, + sdl2::keyboard::Scancode::Apostrophe => Scancode::Apostrophe, + sdl2::keyboard::Scancode::Grave => Scancode::Grave, + sdl2::keyboard::Scancode::Comma => Scancode::Comma, + sdl2::keyboard::Scancode::Period => Scancode::Period, + sdl2::keyboard::Scancode::Slash => Scancode::Slash, + sdl2::keyboard::Scancode::CapsLock => Scancode::CapsLock, + sdl2::keyboard::Scancode::F1 => Scancode::F1, + sdl2::keyboard::Scancode::F2 => Scancode::F2, + sdl2::keyboard::Scancode::F3 => Scancode::F3, + sdl2::keyboard::Scancode::F4 => Scancode::F4, + sdl2::keyboard::Scancode::F5 => Scancode::F5, + sdl2::keyboard::Scancode::F6 => Scancode::F6, + sdl2::keyboard::Scancode::F7 => Scancode::F7, + sdl2::keyboard::Scancode::F8 => Scancode::F8, + sdl2::keyboard::Scancode::F9 => Scancode::F9, + sdl2::keyboard::Scancode::F10 => Scancode::F10, + sdl2::keyboard::Scancode::F11 => Scancode::F11, + sdl2::keyboard::Scancode::F12 => Scancode::F12, + sdl2::keyboard::Scancode::PrintScreen => Scancode::PrintScreen, + sdl2::keyboard::Scancode::ScrollLock => Scancode::ScrollLock, + sdl2::keyboard::Scancode::Pause => Scancode::Pause, + sdl2::keyboard::Scancode::Insert => Scancode::Insert, + sdl2::keyboard::Scancode::Home => Scancode::Home, + sdl2::keyboard::Scancode::PageUp => Scancode::PageUp, + sdl2::keyboard::Scancode::Delete => Scancode::Delete, + sdl2::keyboard::Scancode::End => Scancode::End, + sdl2::keyboard::Scancode::PageDown => Scancode::PageDown, + sdl2::keyboard::Scancode::Right => Scancode::Right, + sdl2::keyboard::Scancode::Left => Scancode::Left, + sdl2::keyboard::Scancode::Down => Scancode::Down, + sdl2::keyboard::Scancode::Up => Scancode::Up, + sdl2::keyboard::Scancode::NumLockClear => Scancode::NumLockClear, + sdl2::keyboard::Scancode::KpDivide => Scancode::KpDivide, + sdl2::keyboard::Scancode::KpMultiply => Scancode::KpMultiply, + sdl2::keyboard::Scancode::KpMinus => Scancode::KpMinus, + sdl2::keyboard::Scancode::KpPlus => Scancode::KpPlus, + sdl2::keyboard::Scancode::KpEnter => Scancode::KpEnter, + sdl2::keyboard::Scancode::Kp1 => Scancode::Kp1, + sdl2::keyboard::Scancode::Kp2 => Scancode::Kp2, + sdl2::keyboard::Scancode::Kp3 => Scancode::Kp3, + sdl2::keyboard::Scancode::Kp4 => Scancode::Kp4, + sdl2::keyboard::Scancode::Kp5 => Scancode::Kp5, + sdl2::keyboard::Scancode::Kp6 => Scancode::Kp6, + sdl2::keyboard::Scancode::Kp7 => Scancode::Kp7, + sdl2::keyboard::Scancode::Kp8 => Scancode::Kp8, + sdl2::keyboard::Scancode::Kp9 => Scancode::Kp9, + sdl2::keyboard::Scancode::Kp0 => Scancode::Kp0, + sdl2::keyboard::Scancode::KpPeriod => Scancode::KpPeriod, + sdl2::keyboard::Scancode::NonUsBackslash => Scancode::NonUsBackslash, + sdl2::keyboard::Scancode::Application => Scancode::Application, + sdl2::keyboard::Scancode::Power => Scancode::Power, + sdl2::keyboard::Scancode::KpEquals => Scancode::KpEquals, + sdl2::keyboard::Scancode::F13 => Scancode::F13, + sdl2::keyboard::Scancode::F14 => Scancode::F14, + sdl2::keyboard::Scancode::F15 => Scancode::F15, + sdl2::keyboard::Scancode::F16 => Scancode::F16, + sdl2::keyboard::Scancode::F17 => Scancode::F17, + sdl2::keyboard::Scancode::F18 => Scancode::F18, + sdl2::keyboard::Scancode::F19 => Scancode::F19, + sdl2::keyboard::Scancode::F20 => Scancode::F20, + sdl2::keyboard::Scancode::F21 => Scancode::F21, + sdl2::keyboard::Scancode::F22 => Scancode::F22, + sdl2::keyboard::Scancode::F23 => Scancode::F23, + sdl2::keyboard::Scancode::F24 => Scancode::F24, + sdl2::keyboard::Scancode::Execute => Scancode::Execute, + sdl2::keyboard::Scancode::Help => Scancode::Help, + sdl2::keyboard::Scancode::Menu => Scancode::Menu, + sdl2::keyboard::Scancode::Select => Scancode::Select, + sdl2::keyboard::Scancode::Stop => Scancode::Stop, + sdl2::keyboard::Scancode::Again => Scancode::Again, + sdl2::keyboard::Scancode::Undo => Scancode::Undo, + sdl2::keyboard::Scancode::Cut => Scancode::Cut, + sdl2::keyboard::Scancode::Copy => Scancode::Copy, + sdl2::keyboard::Scancode::Paste => Scancode::Paste, + sdl2::keyboard::Scancode::Find => Scancode::Find, + sdl2::keyboard::Scancode::Mute => Scancode::Mute, + sdl2::keyboard::Scancode::VolumeUp => Scancode::VolumeUp, + sdl2::keyboard::Scancode::VolumeDown => Scancode::VolumeDown, + sdl2::keyboard::Scancode::KpComma => Scancode::KpComma, + sdl2::keyboard::Scancode::KpEqualsAS400 => Scancode::KpEqualsAS400, + sdl2::keyboard::Scancode::International1 => Scancode::International1, + sdl2::keyboard::Scancode::International2 => Scancode::International2, + sdl2::keyboard::Scancode::International3 => Scancode::International3, + sdl2::keyboard::Scancode::International4 => Scancode::International4, + sdl2::keyboard::Scancode::International5 => Scancode::International5, + sdl2::keyboard::Scancode::International6 => Scancode::International6, + sdl2::keyboard::Scancode::International7 => Scancode::International7, + sdl2::keyboard::Scancode::International8 => Scancode::International8, + sdl2::keyboard::Scancode::International9 => Scancode::International9, + sdl2::keyboard::Scancode::Lang1 => Scancode::Lang1, + sdl2::keyboard::Scancode::Lang2 => Scancode::Lang2, + sdl2::keyboard::Scancode::Lang3 => Scancode::Lang3, + sdl2::keyboard::Scancode::Lang4 => Scancode::Lang4, + sdl2::keyboard::Scancode::Lang5 => Scancode::Lang5, + sdl2::keyboard::Scancode::Lang6 => Scancode::Lang6, + sdl2::keyboard::Scancode::Lang7 => Scancode::Lang7, + sdl2::keyboard::Scancode::Lang8 => Scancode::Lang8, + sdl2::keyboard::Scancode::Lang9 => Scancode::Lang9, + sdl2::keyboard::Scancode::AltErase => Scancode::AltErase, + sdl2::keyboard::Scancode::SysReq => Scancode::SysReq, + sdl2::keyboard::Scancode::Cancel => Scancode::Cancel, + sdl2::keyboard::Scancode::Clear => Scancode::Clear, + sdl2::keyboard::Scancode::Prior => Scancode::Prior, + sdl2::keyboard::Scancode::Return2 => Scancode::Return2, + sdl2::keyboard::Scancode::Separator => Scancode::Separator, + sdl2::keyboard::Scancode::Out => Scancode::Out, + sdl2::keyboard::Scancode::Oper => Scancode::Oper, + sdl2::keyboard::Scancode::ClearAgain => Scancode::ClearAgain, + sdl2::keyboard::Scancode::CrSel => Scancode::CrSel, + sdl2::keyboard::Scancode::ExSel => Scancode::ExSel, + sdl2::keyboard::Scancode::Kp00 => Scancode::Kp00, + sdl2::keyboard::Scancode::Kp000 => Scancode::Kp000, + sdl2::keyboard::Scancode::ThousandsSeparator => Scancode::ThousandsSeparator, + sdl2::keyboard::Scancode::DecimalSeparator => Scancode::DecimalSeparator, + sdl2::keyboard::Scancode::CurrencyUnit => Scancode::CurrencyUnit, + sdl2::keyboard::Scancode::CurrencySubUnit => Scancode::CurrencySubUnit, + sdl2::keyboard::Scancode::KpLeftParen => Scancode::KpLeftParen, + sdl2::keyboard::Scancode::KpRightParen => Scancode::KpRightParen, + sdl2::keyboard::Scancode::KpLeftBrace => Scancode::KpLeftBrace, + sdl2::keyboard::Scancode::KpRightBrace => Scancode::KpRightBrace, + sdl2::keyboard::Scancode::KpTab => Scancode::KpTab, + sdl2::keyboard::Scancode::KpBackspace => Scancode::KpBackspace, + sdl2::keyboard::Scancode::KpA => Scancode::KpA, + sdl2::keyboard::Scancode::KpB => Scancode::KpB, + sdl2::keyboard::Scancode::KpC => Scancode::KpC, + sdl2::keyboard::Scancode::KpD => Scancode::KpD, + sdl2::keyboard::Scancode::KpE => Scancode::KpE, + sdl2::keyboard::Scancode::KpF => Scancode::KpF, + sdl2::keyboard::Scancode::KpXor => Scancode::KpXor, + sdl2::keyboard::Scancode::KpPower => Scancode::KpPower, + sdl2::keyboard::Scancode::KpPercent => Scancode::KpPercent, + sdl2::keyboard::Scancode::KpLess => Scancode::KpLess, + sdl2::keyboard::Scancode::KpGreater => Scancode::KpGreater, + sdl2::keyboard::Scancode::KpAmpersand => Scancode::KpAmpersand, + sdl2::keyboard::Scancode::KpDblAmpersand => Scancode::KpDblAmpersand, + sdl2::keyboard::Scancode::KpVerticalBar => Scancode::KpVerticalBar, + sdl2::keyboard::Scancode::KpDblVerticalBar => Scancode::KpDblVerticalBar, + sdl2::keyboard::Scancode::KpColon => Scancode::KpColon, + sdl2::keyboard::Scancode::KpHash => Scancode::KpHash, + sdl2::keyboard::Scancode::KpSpace => Scancode::KpSpace, + sdl2::keyboard::Scancode::KpAt => Scancode::KpAt, + sdl2::keyboard::Scancode::KpExclam => Scancode::KpExclam, + sdl2::keyboard::Scancode::KpMemStore => Scancode::KpMemStore, + sdl2::keyboard::Scancode::KpMemRecall => Scancode::KpMemRecall, + sdl2::keyboard::Scancode::KpMemClear => Scancode::KpMemClear, + sdl2::keyboard::Scancode::KpMemAdd => Scancode::KpMemAdd, + sdl2::keyboard::Scancode::KpMemSubtract => Scancode::KpMemSubtract, + sdl2::keyboard::Scancode::KpMemMultiply => Scancode::KpMemMultiply, + sdl2::keyboard::Scancode::KpMemDivide => Scancode::KpMemDivide, + sdl2::keyboard::Scancode::KpPlusMinus => Scancode::KpPlusMinus, + sdl2::keyboard::Scancode::KpClear => Scancode::KpClear, + sdl2::keyboard::Scancode::KpClearEntry => Scancode::KpClearEntry, + sdl2::keyboard::Scancode::KpBinary => Scancode::KpBinary, + sdl2::keyboard::Scancode::KpOctal => Scancode::KpOctal, + sdl2::keyboard::Scancode::KpDecimal => Scancode::KpDecimal, + sdl2::keyboard::Scancode::KpHexadecimal => Scancode::KpHexadecimal, + sdl2::keyboard::Scancode::LCtrl => Scancode::LCtrl, + sdl2::keyboard::Scancode::LShift => Scancode::LShift, + sdl2::keyboard::Scancode::LAlt => Scancode::LAlt, + sdl2::keyboard::Scancode::LGui => Scancode::LGui, + sdl2::keyboard::Scancode::RCtrl => Scancode::RCtrl, + sdl2::keyboard::Scancode::RShift => Scancode::RShift, + sdl2::keyboard::Scancode::RAlt => Scancode::RAlt, + sdl2::keyboard::Scancode::RGui => Scancode::RGui, + sdl2::keyboard::Scancode::Mode => Scancode::Mode, + sdl2::keyboard::Scancode::AudioNext => Scancode::AudioNext, + sdl2::keyboard::Scancode::AudioPrev => Scancode::AudioPrev, + sdl2::keyboard::Scancode::AudioStop => Scancode::AudioStop, + sdl2::keyboard::Scancode::AudioPlay => Scancode::AudioPlay, + sdl2::keyboard::Scancode::AudioMute => Scancode::AudioMute, + sdl2::keyboard::Scancode::MediaSelect => Scancode::MediaSelect, + sdl2::keyboard::Scancode::Www => Scancode::Www, + sdl2::keyboard::Scancode::Mail => Scancode::Mail, + sdl2::keyboard::Scancode::Calculator => Scancode::Calculator, + sdl2::keyboard::Scancode::Computer => Scancode::Computer, + sdl2::keyboard::Scancode::AcSearch => Scancode::AcSearch, + sdl2::keyboard::Scancode::AcHome => Scancode::AcHome, + sdl2::keyboard::Scancode::AcBack => Scancode::AcBack, + sdl2::keyboard::Scancode::AcForward => Scancode::AcForward, + sdl2::keyboard::Scancode::AcStop => Scancode::AcStop, + sdl2::keyboard::Scancode::AcRefresh => Scancode::AcRefresh, + sdl2::keyboard::Scancode::AcBookmarks => Scancode::AcBookmarks, + sdl2::keyboard::Scancode::BrightnessDown => Scancode::BrightnessDown, + sdl2::keyboard::Scancode::BrightnessUp => Scancode::BrightnessUp, + sdl2::keyboard::Scancode::DisplaySwitch => Scancode::DisplaySwitch, + sdl2::keyboard::Scancode::KbdIllumToggle => Scancode::KbdIllumToggle, + sdl2::keyboard::Scancode::KbdIllumDown => Scancode::KbdIllumDown, + sdl2::keyboard::Scancode::KbdIllumUp => Scancode::KbdIllumUp, + sdl2::keyboard::Scancode::Eject => Scancode::Eject, + sdl2::keyboard::Scancode::Sleep => Scancode::Sleep, + sdl2::keyboard::Scancode::App1 => Scancode::App1, + sdl2::keyboard::Scancode::App2 => Scancode::App2, + sdl2::keyboard::Scancode::Num => Scancode::Num, + } + } } \ No newline at end of file diff --git a/libretrogd/src/system/input_devices/mod.rs b/libretrogd/src/system/input_devices/mod.rs index 73d47a8..d540f5d 100644 --- a/libretrogd/src/system/input_devices/mod.rs +++ b/libretrogd/src/system/input_devices/mod.rs @@ -5,41 +5,41 @@ pub mod mouse; #[derive(Clone, Copy, Eq, PartialEq, Debug)] pub enum ButtonState { - Idle, - Pressed, - Held, - Released, + Idle, + Pressed, + Held, + Released, } /// Common trait for input device implementations. pub trait InputDevice { - /// Performs internal house-keeping necessary for properly reporting the current state of this - /// input device. Normally this should be called on the device before all of this frame's - /// input events have been processed via `handle_event`. - fn update(&mut self); + /// Performs internal house-keeping necessary for properly reporting the current state of this + /// input device. Normally this should be called on the device before all of this frame's + /// input events have been processed via `handle_event`. + fn update(&mut self); } /// Container for all available input devices available for applications to use. pub struct InputDevices { - pub keyboard: keyboard::Keyboard, - pub mouse: mouse::Mouse, + pub keyboard: keyboard::Keyboard, + pub mouse: mouse::Mouse, } impl InputDevice for InputDevices { - fn update(&mut self) { - self.keyboard.update(); - self.mouse.update(); - } + fn update(&mut self) { + self.keyboard.update(); + self.mouse.update(); + } } impl SystemEventHandler for InputDevices { - fn handle_event(&mut self, event: &SystemEvent) -> bool { - if self.keyboard.handle_event(event) { - return true; - } - if self.mouse.handle_event(event) { - return true; - } - false - } + fn handle_event(&mut self, event: &SystemEvent) -> bool { + if self.keyboard.handle_event(event) { + return true; + } + if self.mouse.handle_event(event) { + return true; + } + false + } } \ No newline at end of file diff --git a/libretrogd/src/system/input_devices/mouse/buttons.rs b/libretrogd/src/system/input_devices/mouse/buttons.rs index cd7671f..16b16ea 100644 --- a/libretrogd/src/system/input_devices/mouse/buttons.rs +++ b/libretrogd/src/system/input_devices/mouse/buttons.rs @@ -13,23 +13,23 @@ bitflags! { #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] #[repr(u8)] pub enum MouseButton { - Unknown = 0, - Left = sdl2::mouse::MouseButton::Left as u8, - Middle = sdl2::mouse::MouseButton::Middle as u8, - Right = sdl2::mouse::MouseButton::Right as u8, - X1 = sdl2::mouse::MouseButton::X1 as u8, - X2 = sdl2::mouse::MouseButton::X2 as u8, + Unknown = 0, + Left = sdl2::mouse::MouseButton::Left as u8, + Middle = sdl2::mouse::MouseButton::Middle as u8, + Right = sdl2::mouse::MouseButton::Right as u8, + X1 = sdl2::mouse::MouseButton::X1 as u8, + X2 = sdl2::mouse::MouseButton::X2 as u8, } impl From for MouseButton { - fn from(value: sdl2::mouse::MouseButton) -> Self { - match value { - sdl2::mouse::MouseButton::Unknown => MouseButton::Unknown, - sdl2::mouse::MouseButton::Left => MouseButton::Left, - sdl2::mouse::MouseButton::Middle => MouseButton::Middle, - sdl2::mouse::MouseButton::Right => MouseButton::Right, - sdl2::mouse::MouseButton::X1 => MouseButton::X1, - sdl2::mouse::MouseButton::X2 => MouseButton::X2 - } - } + fn from(value: sdl2::mouse::MouseButton) -> Self { + match value { + sdl2::mouse::MouseButton::Unknown => MouseButton::Unknown, + sdl2::mouse::MouseButton::Left => MouseButton::Left, + sdl2::mouse::MouseButton::Middle => MouseButton::Middle, + sdl2::mouse::MouseButton::Right => MouseButton::Right, + sdl2::mouse::MouseButton::X1 => MouseButton::X1, + sdl2::mouse::MouseButton::X2 => MouseButton::X2 + } + } } \ No newline at end of file diff --git a/libretrogd/src/system/input_devices/mouse/mod.rs b/libretrogd/src/system/input_devices/mouse/mod.rs index 992bf8a..1647b2d 100644 --- a/libretrogd/src/system/input_devices/mouse/mod.rs +++ b/libretrogd/src/system/input_devices/mouse/mod.rs @@ -17,22 +17,22 @@ const DEFAULT_MOUSE_CURSOR_HEIGHT: usize = 16; #[rustfmt::skip] const DEFAULT_MOUSE_CURSOR: [u8; DEFAULT_MOUSE_CURSOR_WIDTH * DEFAULT_MOUSE_CURSOR_HEIGHT] = [ - 0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0x00,0x0f,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0x00,0x0f,0x0f,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0x00,0x0f,0x0f,0x0f,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0x00,0x0f,0x0f,0x0f,0x0f,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0x00,0x0f,0x0f,0x0f,0x0f,0x0f,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0x00,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0x00,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0x00,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x00,0xff,0xff,0xff,0xff,0xff,0xff, - 0x00,0x0f,0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff, - 0x00,0x0f,0x0f,0x00,0x0f,0x0f,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0x00,0x0f,0x00,0x00,0x00,0x0f,0x0f,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0x00,0x00,0xff,0xff,0x00,0x0f,0x0f,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0x00,0x0f,0x0f,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0x00,0x0f,0x0f,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x0f, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x0f, 0x0f, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x0f, 0x0f, 0x0f, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x0f, 0x0f, 0x00, 0x0f, 0x0f, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x0f, 0x0f, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x0f, 0x0f, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x0f, 0x0f, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff ]; /// Holds the current state of the mouse. @@ -44,287 +44,287 @@ const DEFAULT_MOUSE_CURSOR: [u8; DEFAULT_MOUSE_CURSOR_WIDTH * DEFAULT_MOUSE_CURS /// [`System`]: crate::System #[derive(Debug)] pub struct Mouse { - x: i32, - y: i32, - x_delta: i32, - y_delta: i32, - buttons: [ButtonState; MAX_BUTTONS], - cursor: Bitmap, - cursor_background: Bitmap, - cursor_hotspot_x: u32, - cursor_hotspot_y: u32, - cursor_enabled: bool, + x: i32, + y: i32, + x_delta: i32, + y_delta: i32, + buttons: [ButtonState; MAX_BUTTONS], + cursor: Bitmap, + cursor_background: Bitmap, + cursor_hotspot_x: u32, + cursor_hotspot_y: u32, + cursor_enabled: bool, } impl Mouse { - pub fn new() -> Mouse { - let (cursor, cursor_background, cursor_hotspot_x, cursor_hotspot_y) = - Self::get_default_mouse_cursor(); + pub fn new() -> Mouse { + let (cursor, cursor_background, cursor_hotspot_x, cursor_hotspot_y) = + Self::get_default_mouse_cursor(); - Mouse { - x: 0, - y: 0, - x_delta: 0, - y_delta: 0, - buttons: [ButtonState::Idle; MAX_BUTTONS], - cursor, - cursor_background, - cursor_hotspot_x, - cursor_hotspot_y, - cursor_enabled: false, - } - } + Mouse { + x: 0, + y: 0, + x_delta: 0, + y_delta: 0, + buttons: [ButtonState::Idle; MAX_BUTTONS], + cursor, + cursor_background, + cursor_hotspot_x, + cursor_hotspot_y, + cursor_enabled: false, + } + } - /// Returns the current x coordinate of the mouse cursor. - #[inline] - pub fn x(&self) -> i32 { - self.x - } + /// Returns the current x coordinate of the mouse cursor. + #[inline] + pub fn x(&self) -> i32 { + self.x + } - /// Returns the current y coordinate of the mouse cursor. - #[inline] - pub fn y(&self) -> i32 { - self.y - } + /// Returns the current y coordinate of the mouse cursor. + #[inline] + pub fn y(&self) -> i32 { + self.y + } - /// Returns the amount of pixels along the x-axis that the mouse cursor moved since the last - /// time that the mouse state was updated. - #[inline] - pub fn x_delta(&self) -> i32 { - self.x_delta - } + /// Returns the amount of pixels along the x-axis that the mouse cursor moved since the last + /// time that the mouse state was updated. + #[inline] + pub fn x_delta(&self) -> i32 { + self.x_delta + } - /// Returns the amount of pixels along the y-axis that the mouse cursor moved since the last - /// time that the mouse state was updated. - #[inline] - pub fn y_delta(&self) -> i32 { - self.y_delta - } + /// Returns the amount of pixels along the y-axis that the mouse cursor moved since the last + /// time that the mouse state was updated. + #[inline] + pub fn y_delta(&self) -> i32 { + self.y_delta + } - /// Returns true if the given button was just pressed or is being held down. - #[inline] - pub fn is_button_down(&self, button: usize) -> bool { - matches!( + /// Returns true if the given button was just pressed or is being held down. + #[inline] + pub fn is_button_down(&self, button: usize) -> bool { + matches!( self.buttons[button], ButtonState::Pressed | ButtonState::Held ) - } + } - /// Returns true if the given button was not just pressed and is not being held down. - #[inline] - pub fn is_button_up(&self, button: usize) -> bool { - matches!( + /// Returns true if the given button was not just pressed and is not being held down. + #[inline] + pub fn is_button_up(&self, button: usize) -> bool { + matches!( self.buttons[button], ButtonState::Released | ButtonState::Idle ) - } + } - /// Returns true if the given button was just pressed (not being held down, yet). - #[inline] - pub fn is_button_pressed(&self, button: usize) -> bool { - self.buttons[button] == ButtonState::Pressed - } + /// Returns true if the given button was just pressed (not being held down, yet). + #[inline] + pub fn is_button_pressed(&self, button: usize) -> bool { + self.buttons[button] == ButtonState::Pressed + } - /// Returns true if the given button was just released. - #[inline] - pub fn is_button_released(&self, button: usize) -> bool { - self.buttons[button] == ButtonState::Released - } + /// Returns true if the given button was just released. + #[inline] + pub fn is_button_released(&self, button: usize) -> bool { + self.buttons[button] == ButtonState::Released + } - /// Returns a reference to the current mouse cursor bitmap. - #[inline] - pub fn cursor_bitmap(&self) -> &Bitmap { - &self.cursor - } + /// Returns a reference to the current mouse cursor bitmap. + #[inline] + pub fn cursor_bitmap(&self) -> &Bitmap { + &self.cursor + } - /// Returns the current mouse cursor's "hotspot" x coordinate. - #[inline] - pub fn cursor_hotspot_x(&self) -> u32 { - self.cursor_hotspot_x - } + /// Returns the current mouse cursor's "hotspot" x coordinate. + #[inline] + pub fn cursor_hotspot_x(&self) -> u32 { + self.cursor_hotspot_x + } - /// Returns the current mouse cursor's "hotspot" y coordinate. - #[inline] - pub fn cursor_hotspot_y(&self) -> u32 { - self.cursor_hotspot_y - } + /// Returns the current mouse cursor's "hotspot" y coordinate. + #[inline] + pub fn cursor_hotspot_y(&self) -> u32 { + self.cursor_hotspot_y + } - /// Returns true if mouse cursor bitmap rendering is enabled. - #[inline] - pub fn is_cursor_enabled(&self) -> bool { - self.cursor_enabled - } + /// Returns true if mouse cursor bitmap rendering is enabled. + #[inline] + pub fn is_cursor_enabled(&self) -> bool { + self.cursor_enabled + } - /// Enables or disables mouse cursor bitmap rendering. - #[inline] - pub fn enable_cursor(&mut self, enable: bool) { - self.cursor_enabled = enable; - } + /// Enables or disables mouse cursor bitmap rendering. + #[inline] + pub fn enable_cursor(&mut self, enable: bool) { + self.cursor_enabled = enable; + } - /// Sets the [`Bitmap`] used to display the mouse cursor and the "hotspot" coordinate. The - /// bitmap provided here should be set up to use color 255 as the transparent color. - /// - /// # Arguments - /// - /// * `cursor`: the bitmap to be used to display the mouse cursor on screen - /// * `hotspot_x`: the "hotspot" x coordinate - /// * `hotspot_y`: the "hotspot" y coordinate. - pub fn set_mouse_cursor(&mut self, cursor: Bitmap, hotspot_x: u32, hotspot_y: u32) { - self.cursor = cursor; - self.cursor_background = Bitmap::new(self.cursor.width(), self.cursor.height()).unwrap(); - self.cursor_hotspot_x = hotspot_x; - self.cursor_hotspot_y = hotspot_y; - } + /// Sets the [`Bitmap`] used to display the mouse cursor and the "hotspot" coordinate. The + /// bitmap provided here should be set up to use color 255 as the transparent color. + /// + /// # Arguments + /// + /// * `cursor`: the bitmap to be used to display the mouse cursor on screen + /// * `hotspot_x`: the "hotspot" x coordinate + /// * `hotspot_y`: the "hotspot" y coordinate. + pub fn set_mouse_cursor(&mut self, cursor: Bitmap, hotspot_x: u32, hotspot_y: u32) { + self.cursor = cursor; + self.cursor_background = Bitmap::new(self.cursor.width(), self.cursor.height()).unwrap(); + self.cursor_hotspot_x = hotspot_x; + self.cursor_hotspot_y = hotspot_y; + } - /// Resets the mouse cursor bitmap and "hotspot" coordinate back to the default settings. - pub fn set_default_mouse_cursor(&mut self) { - let (cursor, background, hotspot_x, hotspot_y) = Self::get_default_mouse_cursor(); - self.cursor = cursor; - self.cursor_background = background; - self.cursor_hotspot_x = hotspot_x; - self.cursor_hotspot_y = hotspot_y; - } + /// Resets the mouse cursor bitmap and "hotspot" coordinate back to the default settings. + pub fn set_default_mouse_cursor(&mut self) { + let (cursor, background, hotspot_x, hotspot_y) = Self::get_default_mouse_cursor(); + self.cursor = cursor; + self.cursor_background = background; + self.cursor_hotspot_x = hotspot_x; + self.cursor_hotspot_y = hotspot_y; + } - fn get_default_mouse_cursor() -> (Bitmap, Bitmap, u32, u32) { - let mut cursor = Bitmap::new( - DEFAULT_MOUSE_CURSOR_WIDTH as u32, - DEFAULT_MOUSE_CURSOR_HEIGHT as u32, - ) - .unwrap(); - cursor.pixels_mut().copy_from_slice(&DEFAULT_MOUSE_CURSOR); + fn get_default_mouse_cursor() -> (Bitmap, Bitmap, u32, u32) { + let mut cursor = Bitmap::new( + DEFAULT_MOUSE_CURSOR_WIDTH as u32, + DEFAULT_MOUSE_CURSOR_HEIGHT as u32, + ) + .unwrap(); + cursor.pixels_mut().copy_from_slice(&DEFAULT_MOUSE_CURSOR); - let cursor_background = Bitmap::new(cursor.width(), cursor.height()).unwrap(); + let cursor_background = Bitmap::new(cursor.width(), cursor.height()).unwrap(); - ( - cursor, - cursor_background, - DEFAULT_MOUSE_CURSOR_HOTSPOT_X, - DEFAULT_MOUSE_CURSOR_HOTSPOT_Y, - ) - } + ( + cursor, + cursor_background, + DEFAULT_MOUSE_CURSOR_HOTSPOT_X, + DEFAULT_MOUSE_CURSOR_HOTSPOT_Y, + ) + } - #[inline] - fn get_cursor_render_position(&self) -> (i32, i32) { - ( - self.x - self.cursor_hotspot_x as i32, - self.y - self.cursor_hotspot_y as i32, - ) - } + #[inline] + fn get_cursor_render_position(&self) -> (i32, i32) { + ( + self.x - self.cursor_hotspot_x as i32, + self.y - self.cursor_hotspot_y as i32, + ) + } - /// Renders the mouse cursor bitmap onto the destination bitmap at the mouse's current - /// position. The destination bitmap specified is assumed to be the [`System`]'s video - /// backbuffer bitmap. The background on the destination bitmap is saved internally and a - /// subsequent call to [`Mouse::hide_cursor`] will restore the background. - /// - /// If mouse cursor rendering is not currently enabled, this method does nothing. - /// - /// Applications will not normally need to call this method, as if mouse cursor rendering is - /// enabled, this will be automatically handled by [`System::display`]. - /// - /// [`System`]: crate::System - /// [`System::display`]: crate::System::display - pub fn render_cursor(&mut self, dest: &mut Bitmap) { - if !self.cursor_enabled { - return; - } + /// Renders the mouse cursor bitmap onto the destination bitmap at the mouse's current + /// position. The destination bitmap specified is assumed to be the [`System`]'s video + /// backbuffer bitmap. The background on the destination bitmap is saved internally and a + /// subsequent call to [`Mouse::hide_cursor`] will restore the background. + /// + /// If mouse cursor rendering is not currently enabled, this method does nothing. + /// + /// Applications will not normally need to call this method, as if mouse cursor rendering is + /// enabled, this will be automatically handled by [`System::display`]. + /// + /// [`System`]: crate::System + /// [`System::display`]: crate::System::display + pub fn render_cursor(&mut self, dest: &mut Bitmap) { + if !self.cursor_enabled { + return; + } - let (x, y) = self.get_cursor_render_position(); + let (x, y) = self.get_cursor_render_position(); - // preserve existing background first - self.cursor_background.blit_region( - BlitMethod::Solid, - &dest, - &Rect::new(x, y, self.cursor.width(), self.cursor.height()), - 0, - 0, - ); + // preserve existing background first + self.cursor_background.blit_region( + BlitMethod::Solid, + &dest, + &Rect::new(x, y, self.cursor.width(), self.cursor.height()), + 0, + 0, + ); - dest.blit(BlitMethod::Transparent(255), &self.cursor, x, y); - } + dest.blit(BlitMethod::Transparent(255), &self.cursor, x, y); + } - /// Restores the original destination bitmap contents where the mouse cursor bitmap was - /// rendered to during the previous call to [`Mouse::render_cursor`]. The destination bitmap - /// specified is assumed to be the [`System`]'s video backbuffer bitmap. - /// - /// If mouse cursor rendering is not currently enabled, this method does nothing. - /// - /// Applications will not normally need to call this method, as if mouse cursor rendering is - /// enabled, this will be automatically handled by [`System::display`]. - /// - /// [`System`]: crate::System - /// [`System::display`]: crate::System::display - pub fn hide_cursor(&mut self, dest: &mut Bitmap) { - if !self.cursor_enabled { - return; - } + /// Restores the original destination bitmap contents where the mouse cursor bitmap was + /// rendered to during the previous call to [`Mouse::render_cursor`]. The destination bitmap + /// specified is assumed to be the [`System`]'s video backbuffer bitmap. + /// + /// If mouse cursor rendering is not currently enabled, this method does nothing. + /// + /// Applications will not normally need to call this method, as if mouse cursor rendering is + /// enabled, this will be automatically handled by [`System::display`]. + /// + /// [`System`]: crate::System + /// [`System::display`]: crate::System::display + pub fn hide_cursor(&mut self, dest: &mut Bitmap) { + if !self.cursor_enabled { + return; + } - let (x, y) = self.get_cursor_render_position(); - dest.blit(BlitMethod::Solid, &self.cursor_background, x, y); - } + let (x, y) = self.get_cursor_render_position(); + dest.blit(BlitMethod::Solid, &self.cursor_background, x, y); + } - fn update_button_state(&mut self, button: u32, is_pressed: bool) { - let button_state = &mut self.buttons[button as usize]; - *button_state = if is_pressed { - match *button_state { - ButtonState::Pressed => ButtonState::Held, - ButtonState::Held => ButtonState::Held, - _ => ButtonState::Pressed, - } - } else { - match *button_state { - ButtonState::Pressed | ButtonState::Held => ButtonState::Released, - ButtonState::Released => ButtonState::Idle, - ButtonState::Idle => ButtonState::Idle, - } - } - } + fn update_button_state(&mut self, button: u32, is_pressed: bool) { + let button_state = &mut self.buttons[button as usize]; + *button_state = if is_pressed { + match *button_state { + ButtonState::Pressed => ButtonState::Held, + ButtonState::Held => ButtonState::Held, + _ => ButtonState::Pressed, + } + } else { + match *button_state { + ButtonState::Pressed | ButtonState::Held => ButtonState::Released, + ButtonState::Released => ButtonState::Idle, + ButtonState::Idle => ButtonState::Idle, + } + } + } } impl InputDevice for Mouse { - fn update(&mut self) { - self.x_delta = 0; - self.y_delta = 0; - for state in self.buttons.iter_mut() { - *state = match *state { - ButtonState::Pressed => ButtonState::Held, - ButtonState::Released => ButtonState::Idle, - otherwise => otherwise, - } - } - } + fn update(&mut self) { + self.x_delta = 0; + self.y_delta = 0; + for state in self.buttons.iter_mut() { + *state = match *state { + ButtonState::Pressed => ButtonState::Held, + ButtonState::Released => ButtonState::Idle, + otherwise => otherwise, + } + } + } } impl SystemEventHandler for Mouse { - fn handle_event(&mut self, event: &SystemEvent) -> bool { - match event { - SystemEvent::Mouse(MouseEvent::MouseMotion { - x, - y, - x_delta, - y_delta, - buttons, - }) => { - self.x = *x; - self.y = *y; - self.x_delta = *x_delta; - self.y_delta = *y_delta; + fn handle_event(&mut self, event: &SystemEvent) -> bool { + match event { + SystemEvent::Mouse(MouseEvent::MouseMotion { + x, + y, + x_delta, + y_delta, + buttons, + }) => { + self.x = *x; + self.y = *y; + self.x_delta = *x_delta; + self.y_delta = *y_delta; - self.update_button_state(MouseButton::Left as u32, buttons.contains(MouseButtons::LEFT_BUTTON)); - self.update_button_state(MouseButton::Middle as u32, buttons.contains(MouseButtons::MIDDLE_BUTTON)); - self.update_button_state(MouseButton::Right as u32, buttons.contains(MouseButtons::RIGHT_BUTTON)); - self.update_button_state(MouseButton::X1 as u32, buttons.contains(MouseButtons::X1)); - self.update_button_state(MouseButton::X2 as u32, buttons.contains(MouseButtons::X2)); - true - } - SystemEvent::Mouse(MouseEvent::MouseButtonDown { button, .. }) => { - self.update_button_state(*button as u32, true); - true - } - SystemEvent::Mouse(MouseEvent::MouseButtonUp { button, .. }) => { - self.update_button_state(*button as u32, false); - true - } - _ => false, - } - } + self.update_button_state(MouseButton::Left as u32, buttons.contains(MouseButtons::LEFT_BUTTON)); + self.update_button_state(MouseButton::Middle as u32, buttons.contains(MouseButtons::MIDDLE_BUTTON)); + self.update_button_state(MouseButton::Right as u32, buttons.contains(MouseButtons::RIGHT_BUTTON)); + self.update_button_state(MouseButton::X1 as u32, buttons.contains(MouseButtons::X1)); + self.update_button_state(MouseButton::X2 as u32, buttons.contains(MouseButtons::X2)); + true + } + SystemEvent::Mouse(MouseEvent::MouseButtonDown { button, .. }) => { + self.update_button_state(*button as u32, true); + true + } + SystemEvent::Mouse(MouseEvent::MouseButtonUp { button, .. }) => { + self.update_button_state(*button as u32, false); + true + } + _ => false, + } + } } \ No newline at end of file diff --git a/libretrogd/src/system/mod.rs b/libretrogd/src/system/mod.rs index 4a4f424..654bb06 100644 --- a/libretrogd/src/system/mod.rs +++ b/libretrogd/src/system/mod.rs @@ -20,329 +20,329 @@ pub mod event; pub mod input_devices; fn is_x11_compositor_skipping_problematic() -> bool { - /* - this is _probably_ a bit of a hack. + /* + this is _probably_ a bit of a hack. - currently on linux systems, SDL2 (2.0.8+), tries to "skip" (disable) the X11 server - compositor when starting up. this is to reduce/remove any added latency from the SDL program - that is usually introduced by the compositor when it is enabled for the window. if SDL did - disable the compositor in this manner, it will re-enable it when SDL shuts down. the - intention is for the compositor to be disabled for just the SDL window(s) only and to affect - nothing else running concurrently. + currently on linux systems, SDL2 (2.0.8+), tries to "skip" (disable) the X11 server + compositor when starting up. this is to reduce/remove any added latency from the SDL program + that is usually introduced by the compositor when it is enabled for the window. if SDL did + disable the compositor in this manner, it will re-enable it when SDL shuts down. the + intention is for the compositor to be disabled for just the SDL window(s) only and to affect + nothing else running concurrently. - this works great for several desktop environments, but it unfortunately has a global effect - on KDE/Kwin, where users may notice a visible screen flicker, other concurrently running - applications may exhibit visual artifacts/weirdness, and (all?) other application windows - while the SDL app is running will also have the compositor disabled for them too. + this works great for several desktop environments, but it unfortunately has a global effect + on KDE/Kwin, where users may notice a visible screen flicker, other concurrently running + applications may exhibit visual artifacts/weirdness, and (all?) other application windows + while the SDL app is running will also have the compositor disabled for them too. - not great! this function is a quick, hacky, and probably-not-bullet-proof method to detect - if KDE/Kwin is the current desktop environment. in the future other known problem - configurations could be added here and/or this could/should be updated with a better method - to check for this. - */ - match std::env::consts::OS { - "linux"|"freebsd"|"netbsd"|"openbsd" => { - match std::env::var("XDG_SESSION_DESKTOP") { - Ok(value) => value.eq_ignore_ascii_case("KDE"), - Err(_) => false - } - }, - _ => false, - } + not great! this function is a quick, hacky, and probably-not-bullet-proof method to detect + if KDE/Kwin is the current desktop environment. in the future other known problem + configurations could be added here and/or this could/should be updated with a better method + to check for this. + */ + match std::env::consts::OS { + "linux" | "freebsd" | "netbsd" | "openbsd" => { + match std::env::var("XDG_SESSION_DESKTOP") { + Ok(value) => value.eq_ignore_ascii_case("KDE"), + Err(_) => false + } + } + _ => false, + } } #[derive(Error, Debug)] pub enum SystemError { - #[error("System init error: {0}")] - InitError(String), + #[error("System init error: {0}")] + InitError(String), - #[error("System display error: {0}")] - DisplayError(String), + #[error("System display error: {0}")] + DisplayError(String), - #[error("System audio error: {0}")] - AudioError(#[from] crate::audio::AudioError), + #[error("System audio error: {0}")] + AudioError(#[from] crate::audio::AudioError), } /// Builder for configuring and constructing an instance of [`System`]. #[derive(Debug)] pub struct SystemBuilder { - window_title: String, - vsync: bool, - target_framerate: Option, - initial_scale_factor: u32, - resizable: bool, - show_mouse: bool, - relative_mouse_scaling: bool, - integer_scaling: bool, - skip_x11_compositor: bool, + window_title: String, + vsync: bool, + target_framerate: Option, + initial_scale_factor: u32, + resizable: bool, + show_mouse: bool, + relative_mouse_scaling: bool, + integer_scaling: bool, + skip_x11_compositor: bool, } impl SystemBuilder { - /// Returns a new [`SystemBuilder`] with a default configuration. - pub fn new() -> SystemBuilder { - SystemBuilder { - window_title: String::new(), - vsync: false, - target_framerate: None, - initial_scale_factor: DEFAULT_SCALE_FACTOR, - resizable: true, - show_mouse: false, - relative_mouse_scaling: true, - integer_scaling: false, - skip_x11_compositor: !is_x11_compositor_skipping_problematic(), - } - } + /// Returns a new [`SystemBuilder`] with a default configuration. + pub fn new() -> SystemBuilder { + SystemBuilder { + window_title: String::new(), + vsync: false, + target_framerate: None, + initial_scale_factor: DEFAULT_SCALE_FACTOR, + resizable: true, + show_mouse: false, + relative_mouse_scaling: true, + integer_scaling: false, + skip_x11_compositor: !is_x11_compositor_skipping_problematic(), + } + } - /// Set the window title for the [`System`] to be built. - pub fn window_title(&mut self, window_title: &str) -> &mut SystemBuilder { - self.window_title = window_title.to_string(); - self - } + /// Set the window title for the [`System`] to be built. + pub fn window_title(&mut self, window_title: &str) -> &mut SystemBuilder { + self.window_title = window_title.to_string(); + self + } - /// Enables or disables V-Sync for the [`System`] to be built. Enabling V-sync automatically - /// disables `target_framerate`. - pub fn vsync(&mut self, enable: bool) -> &mut SystemBuilder { - self.vsync = enable; - self.target_framerate = None; - self - } + /// Enables or disables V-Sync for the [`System`] to be built. Enabling V-sync automatically + /// disables `target_framerate`. + pub fn vsync(&mut self, enable: bool) -> &mut SystemBuilder { + self.vsync = enable; + self.target_framerate = None; + self + } - /// Sets a target framerate for the [`System`] being built to run at. This is intended to be - /// used when V-sync is not desired, so setting a target framerate automatically disables - /// `vsync`. - pub fn target_framerate(&mut self, target_framerate: u32) -> &mut SystemBuilder { - self.target_framerate = Some(target_framerate); - self.vsync = false; - self - } + /// Sets a target framerate for the [`System`] being built to run at. This is intended to be + /// used when V-sync is not desired, so setting a target framerate automatically disables + /// `vsync`. + pub fn target_framerate(&mut self, target_framerate: u32) -> &mut SystemBuilder { + self.target_framerate = Some(target_framerate); + self.vsync = false; + self + } - /// Sets an integer scaling factor for the [`System`] being built to up-scale the virtual - /// framebuffer to when displaying it on screen. - pub fn scale_factor(&mut self, scale_factor: u32) -> &mut SystemBuilder { - self.initial_scale_factor = scale_factor; - self - } + /// Sets an integer scaling factor for the [`System`] being built to up-scale the virtual + /// framebuffer to when displaying it on screen. + pub fn scale_factor(&mut self, scale_factor: u32) -> &mut SystemBuilder { + self.initial_scale_factor = scale_factor; + self + } - /// Sets whether the window will be resizable by the user for the [`System`] being built. - pub fn resizable(&mut self, enable: bool) -> &mut SystemBuilder { - self.resizable = enable; - self - } + /// Sets whether the window will be resizable by the user for the [`System`] being built. + pub fn resizable(&mut self, enable: bool) -> &mut SystemBuilder { + self.resizable = enable; + self + } - /// Enables or disables mouse cursor display by the operating system when the cursor is over - /// the window for the [`System`] being built. Disable this if you intend to render your own - /// custom mouse cursor. - pub fn show_mouse(&mut self, enable: bool) -> &mut SystemBuilder { - self.show_mouse = enable; - self - } + /// Enables or disables mouse cursor display by the operating system when the cursor is over + /// the window for the [`System`] being built. Disable this if you intend to render your own + /// custom mouse cursor. + pub fn show_mouse(&mut self, enable: bool) -> &mut SystemBuilder { + self.show_mouse = enable; + self + } - /// Enables or disables automatic DPI scaling of mouse relative movement values (delta values) - /// available via the [`Mouse`] input device. - pub fn relative_mouse_scaling(&mut self, enable: bool) -> &mut SystemBuilder { - self.relative_mouse_scaling = enable; - self - } + /// Enables or disables automatic DPI scaling of mouse relative movement values (delta values) + /// available via the [`Mouse`] input device. + pub fn relative_mouse_scaling(&mut self, enable: bool) -> &mut SystemBuilder { + self.relative_mouse_scaling = enable; + self + } - /// Enables or disables restricting the final rendered output to always be integer scaled, - /// even if that result will not fully fill the area of the window. - pub fn integer_scaling(&mut self, enable: bool) -> &mut SystemBuilder { - self.integer_scaling = enable; - self - } + /// Enables or disables restricting the final rendered output to always be integer scaled, + /// even if that result will not fully fill the area of the window. + pub fn integer_scaling(&mut self, enable: bool) -> &mut SystemBuilder { + self.integer_scaling = enable; + self + } - /// Enables or disables skipping the X11 server compositor on Linux systems only. This can be - /// set to manually control the underlying SDL hint that is used to control this setting. The - /// default setting that [`SystemBuilder`] configures is to follow the SDL default, except where - /// the setting affects the system globally (in certain desktop environments, e.g. KDE/Kwin) - /// which may be undesired by end-users, at the cost of some additional input latency. - pub fn skip_x11_compositor(&mut self, enable: bool) -> &mut SystemBuilder { - self.skip_x11_compositor = enable; - self - } + /// Enables or disables skipping the X11 server compositor on Linux systems only. This can be + /// set to manually control the underlying SDL hint that is used to control this setting. The + /// default setting that [`SystemBuilder`] configures is to follow the SDL default, except where + /// the setting affects the system globally (in certain desktop environments, e.g. KDE/Kwin) + /// which may be undesired by end-users, at the cost of some additional input latency. + pub fn skip_x11_compositor(&mut self, enable: bool) -> &mut SystemBuilder { + self.skip_x11_compositor = enable; + self + } - /// Builds and returns a [`System`] from the current configuration. - pub fn build(&self) -> Result { - // todo: maybe let this be customized in the future, or at least halved so a 160x120 mode can be available ... ? - let screen_width = SCREEN_WIDTH; - let screen_height = SCREEN_HEIGHT; - let texture_pixel_size = 4; // 32-bit ARGB format + /// Builds and returns a [`System`] from the current configuration. + pub fn build(&self) -> Result { + // todo: maybe let this be customized in the future, or at least halved so a 160x120 mode can be available ... ? + let screen_width = SCREEN_WIDTH; + let screen_height = SCREEN_HEIGHT; + let texture_pixel_size = 4; // 32-bit ARGB format - sdl2::hint::set( - "SDL_MOUSE_RELATIVE_SCALING", - if self.relative_mouse_scaling { - "1" - } else { - "0" - }, - ); + sdl2::hint::set( + "SDL_MOUSE_RELATIVE_SCALING", + if self.relative_mouse_scaling { + "1" + } else { + "0" + }, + ); - sdl2::hint::set( - "SDL_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR", - if self.skip_x11_compositor { - "1" - } else { - "0" - } - ); + sdl2::hint::set( + "SDL_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR", + if self.skip_x11_compositor { + "1" + } else { + "0" + }, + ); - // build all the individual SDL subsystems + // build all the individual SDL subsystems - let sdl_context = match sdl2::init() { - Ok(sdl_context) => sdl_context, - Err(message) => return Err(SystemError::InitError(message)), - }; + let sdl_context = match sdl2::init() { + Ok(sdl_context) => sdl_context, + Err(message) => return Err(SystemError::InitError(message)), + }; - let sdl_timer_subsystem = match sdl_context.timer() { - Ok(timer_subsystem) => timer_subsystem, - Err(message) => return Err(SystemError::InitError(message)), - }; + let sdl_timer_subsystem = match sdl_context.timer() { + Ok(timer_subsystem) => timer_subsystem, + Err(message) => return Err(SystemError::InitError(message)), + }; - let sdl_video_subsystem = match sdl_context.video() { - Ok(video_subsystem) => video_subsystem, - Err(message) => return Err(SystemError::InitError(message)), - }; + let sdl_video_subsystem = match sdl_context.video() { + Ok(video_subsystem) => video_subsystem, + Err(message) => return Err(SystemError::InitError(message)), + }; - let sdl_event_pump = match sdl_context.event_pump() { - Ok(event_pump) => event_pump, - Err(message) => return Err(SystemError::InitError(message)), - }; + let sdl_event_pump = match sdl_context.event_pump() { + Ok(event_pump) => event_pump, + Err(message) => return Err(SystemError::InitError(message)), + }; - let sdl_audio_subsystem = match sdl_context.audio() { - Ok(audio_subsystem) => audio_subsystem, - Err(message) => return Err(SystemError::InitError(message)), - }; + let sdl_audio_subsystem = match sdl_context.audio() { + Ok(audio_subsystem) => audio_subsystem, + Err(message) => return Err(SystemError::InitError(message)), + }; - // create the window + // create the window - let window_width = screen_width * self.initial_scale_factor; - let window_height = screen_height * self.initial_scale_factor; - let mut window_builder = &mut (sdl_video_subsystem.window( - self.window_title.as_str(), - window_width, - window_height, - )); - if self.resizable { - window_builder = window_builder.resizable(); - } - let sdl_window = match window_builder.build() { - Ok(window) => window, - Err(error) => return Err(SystemError::InitError(error.to_string())), - }; + let window_width = screen_width * self.initial_scale_factor; + let window_height = screen_height * self.initial_scale_factor; + let mut window_builder = &mut (sdl_video_subsystem.window( + self.window_title.as_str(), + window_width, + window_height, + )); + if self.resizable { + window_builder = window_builder.resizable(); + } + let sdl_window = match window_builder.build() { + Ok(window) => window, + Err(error) => return Err(SystemError::InitError(error.to_string())), + }; - sdl_context.mouse().show_cursor(self.show_mouse); + sdl_context.mouse().show_cursor(self.show_mouse); - // turn the window into a canvas (under the hood, an SDL Renderer that owns the window) + // turn the window into a canvas (under the hood, an SDL Renderer that owns the window) - let mut canvas_builder = sdl_window.into_canvas(); - if self.vsync { - canvas_builder = canvas_builder.present_vsync(); - } - let mut sdl_canvas = match canvas_builder.build() { - Ok(canvas) => canvas, - Err(error) => return Err(SystemError::InitError(error.to_string())), - }; - if let Err(error) = sdl_canvas.set_logical_size(screen_width, screen_height) { - return Err(SystemError::InitError(error.to_string())); - }; + let mut canvas_builder = sdl_window.into_canvas(); + if self.vsync { + canvas_builder = canvas_builder.present_vsync(); + } + let mut sdl_canvas = match canvas_builder.build() { + Ok(canvas) => canvas, + Err(error) => return Err(SystemError::InitError(error.to_string())), + }; + if let Err(error) = sdl_canvas.set_logical_size(screen_width, screen_height) { + return Err(SystemError::InitError(error.to_string())); + }; - // TODO: newer versions of rust-sdl2 support this directly off the WindowCanvas struct - unsafe { - sdl2::sys::SDL_RenderSetIntegerScale( - sdl_canvas.raw(), - if self.integer_scaling { - sdl2::sys::SDL_bool::SDL_TRUE - } else { - sdl2::sys::SDL_bool::SDL_FALSE - }, - ); - } + // TODO: newer versions of rust-sdl2 support this directly off the WindowCanvas struct + unsafe { + sdl2::sys::SDL_RenderSetIntegerScale( + sdl_canvas.raw(), + if self.integer_scaling { + sdl2::sys::SDL_bool::SDL_TRUE + } else { + sdl2::sys::SDL_bool::SDL_FALSE + }, + ); + } - // create an SDL texture which we will be uploading to every frame to display the - // application's framebuffer + // create an SDL texture which we will be uploading to every frame to display the + // application's framebuffer - let sdl_texture = match sdl_canvas.create_texture_streaming( - Some(PixelFormatEnum::ARGB8888), - screen_width, - screen_height, - ) { - Ok(texture) => texture, - Err(error) => return Err(SystemError::InitError(error.to_string())), - }; - let sdl_texture_pitch = (sdl_texture.query().width * texture_pixel_size) as usize; + let sdl_texture = match sdl_canvas.create_texture_streaming( + Some(PixelFormatEnum::ARGB8888), + screen_width, + screen_height, + ) { + Ok(texture) => texture, + Err(error) => return Err(SystemError::InitError(error.to_string())), + }; + let sdl_texture_pitch = (sdl_texture.query().width * texture_pixel_size) as usize; - // create a raw 32-bit RGBA buffer that will be used as the temporary source for - // SDL texture uploads each frame. necessary as applications are dealing with 8-bit indexed - // bitmaps, not 32-bit RGBA pixels, so this temporary buffer is where we convert the final - // application framebuffer to 32-bit RGBA pixels before it is uploaded to the SDL texture - let texture_pixels_size = (screen_width * screen_height * texture_pixel_size) as usize; - let texture_pixels = vec![0u32; texture_pixels_size].into_boxed_slice(); + // create a raw 32-bit RGBA buffer that will be used as the temporary source for + // SDL texture uploads each frame. necessary as applications are dealing with 8-bit indexed + // bitmaps, not 32-bit RGBA pixels, so this temporary buffer is where we convert the final + // application framebuffer to 32-bit RGBA pixels before it is uploaded to the SDL texture + let texture_pixels_size = (screen_width * screen_height * texture_pixel_size) as usize; + let texture_pixels = vec![0u32; texture_pixels_size].into_boxed_slice(); - // create the Bitmap object that will be exposed to the application acting as the system - // backbuffer + // create the Bitmap object that will be exposed to the application acting as the system + // backbuffer - let framebuffer = match Bitmap::new(SCREEN_WIDTH, SCREEN_HEIGHT) { - Ok(bmp) => bmp, - Err(error) => return Err(SystemError::InitError(error.to_string())), - }; + let framebuffer = match Bitmap::new(SCREEN_WIDTH, SCREEN_HEIGHT) { + Ok(bmp) => bmp, + Err(error) => return Err(SystemError::InitError(error.to_string())), + }; - // create the default palette, initialized to the VGA default palette. also exposed to the - // application for manipulation + // create the default palette, initialized to the VGA default palette. also exposed to the + // application for manipulation - let palette = match Palette::new_vga_palette() { - Ok(palette) => palette, - Err(error) => return Err(SystemError::InitError(error.to_string())), - }; + let palette = match Palette::new_vga_palette() { + Ok(palette) => palette, + Err(error) => return Err(SystemError::InitError(error.to_string())), + }; - // create the default font, initialized to the VGA BIOS default font. + // create the default font, initialized to the VGA BIOS default font. - let font = match BitmaskFont::new_vga_font() { - Ok(font) => font, - Err(error) => return Err(SystemError::InitError(error.to_string())), - }; + let font = match BitmaskFont::new_vga_font() { + Ok(font) => font, + Err(error) => return Err(SystemError::InitError(error.to_string())), + }; - let audio_spec = AudioSpecDesired { - freq: Some(TARGET_AUDIO_FREQUENCY as i32), - channels: Some(TARGET_AUDIO_CHANNELS), - samples: None, - }; - let mut audio = Audio::new(audio_spec, &sdl_audio_subsystem)?; - audio.resume(); - let audio_queue = AudioQueue::new(&audio); + let audio_spec = AudioSpecDesired { + freq: Some(TARGET_AUDIO_FREQUENCY as i32), + channels: Some(TARGET_AUDIO_CHANNELS), + samples: None, + }; + let mut audio = Audio::new(audio_spec, &sdl_audio_subsystem)?; + audio.resume(); + let audio_queue = AudioQueue::new(&audio); - let event_pump = SystemEventPump::from(sdl_event_pump); + let event_pump = SystemEventPump::from(sdl_event_pump); - // create input device objects, exposed to the application + // create input device objects, exposed to the application - let keyboard = Keyboard::new(); - let mouse = Mouse::new(); + let keyboard = Keyboard::new(); + let mouse = Mouse::new(); - let input_devices = InputDevices { - keyboard, - mouse, - }; + let input_devices = InputDevices { + keyboard, + mouse, + }; - Ok(System { - sdl_context, - sdl_audio_subsystem, - sdl_video_subsystem, - sdl_timer_subsystem, - sdl_canvas, - sdl_texture, - sdl_texture_pitch, - texture_pixels, - audio, - audio_queue, - video: framebuffer, - palette, - font, - input_devices, - event_pump, - target_framerate: self.target_framerate, - target_framerate_delta: None, - next_tick: 0, - }) - } + Ok(System { + sdl_context, + sdl_audio_subsystem, + sdl_video_subsystem, + sdl_timer_subsystem, + sdl_canvas, + sdl_texture, + sdl_texture_pitch, + texture_pixels, + audio, + audio_queue, + video: framebuffer, + palette, + font, + input_devices, + event_pump, + target_framerate: self.target_framerate, + target_framerate_delta: None, + next_tick: 0, + }) + } } /// Holds all primary structures necessary for interacting with the operating system and for @@ -350,200 +350,200 @@ impl SystemBuilder { /// "virtual machine" exposed by this library. #[allow(dead_code)] pub struct System { - sdl_context: Sdl, - sdl_audio_subsystem: AudioSubsystem, - sdl_video_subsystem: VideoSubsystem, - sdl_timer_subsystem: TimerSubsystem, - sdl_canvas: WindowCanvas, - sdl_texture: Texture, - sdl_texture_pitch: usize, + sdl_context: Sdl, + sdl_audio_subsystem: AudioSubsystem, + sdl_video_subsystem: VideoSubsystem, + sdl_timer_subsystem: TimerSubsystem, + sdl_canvas: WindowCanvas, + sdl_texture: Texture, + sdl_texture_pitch: usize, - texture_pixels: Box<[u32]>, + texture_pixels: Box<[u32]>, - target_framerate: Option, - target_framerate_delta: Option, - next_tick: i64, + target_framerate: Option, + target_framerate_delta: Option, + next_tick: i64, - /// An [`Audio`] instance that allows interacting with the system's audio output device. - pub audio: Audio, + /// An [`Audio`] instance that allows interacting with the system's audio output device. + pub audio: Audio, - /// An [`AudioQueue`] instance that can queue up playback/stop commands to be issued to the - /// system's [`Audio`] instance a bit more flexibly. If you use this, your application must - /// manually call [`AudioQueue::apply`] or [`AudioQueue::apply_to_device`] in your loop to - /// flush the queued commands, otherwise this queue will not do anything. - pub audio_queue: AudioQueue, + /// An [`AudioQueue`] instance that can queue up playback/stop commands to be issued to the + /// system's [`Audio`] instance a bit more flexibly. If you use this, your application must + /// manually call [`AudioQueue::apply`] or [`AudioQueue::apply_to_device`] in your loop to + /// flush the queued commands, otherwise this queue will not do anything. + pub audio_queue: AudioQueue, - /// The primary backbuffer [`Bitmap`] that will be rendered to the screen whenever - /// [`System::display`] is called. Regardless of the actual window size, this bitmap is always - /// [`SCREEN_WIDTH`]x[`SCREEN_HEIGHT`] pixels in size. - pub video: Bitmap, + /// The primary backbuffer [`Bitmap`] that will be rendered to the screen whenever + /// [`System::display`] is called. Regardless of the actual window size, this bitmap is always + /// [`SCREEN_WIDTH`]x[`SCREEN_HEIGHT`] pixels in size. + pub video: Bitmap, - /// The [`Palette`] that will be used in conjunction with the `video` backbuffer to - /// render the final output to the screen whenever [`System::display`] is called. - pub palette: Palette, + /// The [`Palette`] that will be used in conjunction with the `video` backbuffer to + /// render the final output to the screen whenever [`System::display`] is called. + pub palette: Palette, - /// A pre-loaded [`Font`] that can be used for text rendering. - pub font: BitmaskFont, + /// A pre-loaded [`Font`] that can be used for text rendering. + pub font: BitmaskFont, - /// Contains instances representing the current state of the input devices available. - /// To ensure these are updated each frame, ensure that you are either calling - /// [`System::do_events`] or manually implementing an event polling loop which calls - /// [`InputDevices::update`] and [`InputDevices::handle_event`]. - pub input_devices: InputDevices, + /// Contains instances representing the current state of the input devices available. + /// To ensure these are updated each frame, ensure that you are either calling + /// [`System::do_events`] or manually implementing an event polling loop which calls + /// [`InputDevices::update`] and [`InputDevices::handle_event`]. + pub input_devices: InputDevices, - pub event_pump: SystemEventPump, + pub event_pump: SystemEventPump, } impl std::fmt::Debug for System { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("System") - .field("audio", &self.audio) - .field("audio_queue", &self.audio_queue) - .field("video", &self.video) - .field("palette", &self.palette) - .field("font", &self.font) - //.field("keyboard", &self.keyboard) - //.field("mouse", &self.mouse) - .field("target_framerate", &self.target_framerate) - .field("target_framerate_delta", &self.target_framerate_delta) - .field("next_tick", &self.next_tick) - .finish_non_exhaustive() - } + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("System") + .field("audio", &self.audio) + .field("audio_queue", &self.audio_queue) + .field("video", &self.video) + .field("palette", &self.palette) + .field("font", &self.font) + //.field("keyboard", &self.keyboard) + //.field("mouse", &self.mouse) + .field("target_framerate", &self.target_framerate) + .field("target_framerate_delta", &self.target_framerate_delta) + .field("next_tick", &self.next_tick) + .finish_non_exhaustive() + } } impl System { - /// Takes the `video` backbuffer bitmap and `palette` and renders it to the window, up-scaled - /// to fill the window (preserving aspect ratio of course). If V-sync is enabled, this method - /// will block to wait for V-sync. Otherwise, if a target framerate was configured a delay - /// might be used to try to meet that framerate. - pub fn display(&mut self) -> Result<(), SystemError> { - self.input_devices.mouse.render_cursor(&mut self.video); + /// Takes the `video` backbuffer bitmap and `palette` and renders it to the window, up-scaled + /// to fill the window (preserving aspect ratio of course). If V-sync is enabled, this method + /// will block to wait for V-sync. Otherwise, if a target framerate was configured a delay + /// might be used to try to meet that framerate. + pub fn display(&mut self) -> Result<(), SystemError> { + self.input_devices.mouse.render_cursor(&mut self.video); - // convert application framebuffer to 32-bit RGBA pixels, and then upload it to the SDL - // texture so it will be displayed on screen + // convert application framebuffer to 32-bit RGBA pixels, and then upload it to the SDL + // texture so it will be displayed on screen - self.video - .copy_as_argb_to(&mut self.texture_pixels, &self.palette); + self.video + .copy_as_argb_to(&mut self.texture_pixels, &self.palette); - let texture_pixels = self.texture_pixels.as_byte_slice(); - if let Err(error) = self - .sdl_texture - .update(None, texture_pixels, self.sdl_texture_pitch) - { - return Err(SystemError::DisplayError(error.to_string())); - } - self.sdl_canvas.clear(); - if let Err(error) = self.sdl_canvas.copy(&self.sdl_texture, None, None) { - return Err(SystemError::DisplayError(error)); - } - self.sdl_canvas.present(); + let texture_pixels = self.texture_pixels.as_byte_slice(); + if let Err(error) = self + .sdl_texture + .update(None, texture_pixels, self.sdl_texture_pitch) + { + return Err(SystemError::DisplayError(error.to_string())); + } + self.sdl_canvas.clear(); + if let Err(error) = self.sdl_canvas.copy(&self.sdl_texture, None, None) { + return Err(SystemError::DisplayError(error)); + } + self.sdl_canvas.present(); - self.input_devices.mouse.hide_cursor(&mut self.video); + self.input_devices.mouse.hide_cursor(&mut self.video); - // if a specific target framerate is desired, apply some loop timing/delay to achieve it - // TODO: do this better. delaying when running faster like this is a poor way to do this.. + // if a specific target framerate is desired, apply some loop timing/delay to achieve it + // TODO: do this better. delaying when running faster like this is a poor way to do this.. - if let Some(target_framerate) = self.target_framerate { - if self.target_framerate_delta.is_some() { - // normal path for every other loop iteration except the first - let delay = self.next_tick - self.ticks() as i64; - if delay < 0 { - // this loop iteration took too long, no need to delay - self.next_tick -= delay; - } else { - // this loop iteration completed before next_tick time, delay by the remainder - // time period so we're running at about the desired framerate - self.delay(((delay * 1000) / self.tick_frequency() as i64) as u32); - } - } else { - // this branch will occur on the first main loop iteration. we use the fact that - // target_framerate_delta was not yet set to avoid doing any delay on the first - // loop, just in case there was some other processing between the System struct - // being created and the actual beginning of the first loop ... - self.target_framerate_delta = - Some((self.tick_frequency() / target_framerate as u64) as i64); - } + if let Some(target_framerate) = self.target_framerate { + if self.target_framerate_delta.is_some() { + // normal path for every other loop iteration except the first + let delay = self.next_tick - self.ticks() as i64; + if delay < 0 { + // this loop iteration took too long, no need to delay + self.next_tick -= delay; + } else { + // this loop iteration completed before next_tick time, delay by the remainder + // time period so we're running at about the desired framerate + self.delay(((delay * 1000) / self.tick_frequency() as i64) as u32); + } + } else { + // this branch will occur on the first main loop iteration. we use the fact that + // target_framerate_delta was not yet set to avoid doing any delay on the first + // loop, just in case there was some other processing between the System struct + // being created and the actual beginning of the first loop ... + self.target_framerate_delta = + Some((self.tick_frequency() / target_framerate as u64) as i64); + } - // expected time for the next display() call to happen by - self.next_tick = (self.ticks() as i64) + self.target_framerate_delta.unwrap(); - } + // expected time for the next display() call to happen by + self.next_tick = (self.ticks() as i64) + self.target_framerate_delta.unwrap(); + } - Ok(()) - } + Ok(()) + } - /// Checks for and responds to all SDL2 events waiting in the queue. Each event is passed to - /// all [`InputDevice`]'s automatically to ensure input device state is up to date. Returns - /// true if a [`SystemEvent::Quit`] event is encountered, in which case, the application - /// should quit. Otherwise, returns false. - /// - /// ```no_run - /// use libretrogd::system::*; - /// - /// let mut system = SystemBuilder::new().window_title("Example").build().unwrap(); - /// - /// while !system.do_events() { - /// // ... the body of your main loop here ... - /// } - /// ``` - /// - /// If your application needs to react to [`SystemEvent`]s, then instead of using - /// [`System::do_events`], you should instead manually take care of event polling in your - /// main loop. For example: - /// - /// ```no_run - /// use libretrogd::system::*; - /// - /// let mut system = SystemBuilder::new().window_title("Example").build().unwrap(); - /// - /// 'mainloop: loop { - /// system.input_devices.update(); - /// for event in system.event_pump.poll_iter() { - /// system.input_devices.handle_event(&event); - /// match event { - /// SystemEvent::Quit => { - /// break 'mainloop - /// }, - /// _ => {}, - /// } - /// } - /// - /// // ...the rest of the body of your main loop here ... - /// } - /// ``` - pub fn do_events(&mut self) -> bool { - let mut should_quit = false; - self.input_devices.update(); - for event in self.event_pump.poll_iter() { - self.input_devices.handle_event(&event); - if event == SystemEvent::Quit { - should_quit = true; - } - } - should_quit - } + /// Checks for and responds to all SDL2 events waiting in the queue. Each event is passed to + /// all [`InputDevice`]'s automatically to ensure input device state is up to date. Returns + /// true if a [`SystemEvent::Quit`] event is encountered, in which case, the application + /// should quit. Otherwise, returns false. + /// + /// ```no_run + /// use libretrogd::system::*; + /// + /// let mut system = SystemBuilder::new().window_title("Example").build().unwrap(); + /// + /// while !system.do_events() { + /// // ... the body of your main loop here ... + /// } + /// ``` + /// + /// If your application needs to react to [`SystemEvent`]s, then instead of using + /// [`System::do_events`], you should instead manually take care of event polling in your + /// main loop. For example: + /// + /// ```no_run + /// use libretrogd::system::*; + /// + /// let mut system = SystemBuilder::new().window_title("Example").build().unwrap(); + /// + /// 'mainloop: loop { + /// system.input_devices.update(); + /// for event in system.event_pump.poll_iter() { + /// system.input_devices.handle_event(&event); + /// match event { + /// SystemEvent::Quit => { + /// break 'mainloop + /// }, + /// _ => {}, + /// } + /// } + /// + /// // ...the rest of the body of your main loop here ... + /// } + /// ``` + pub fn do_events(&mut self) -> bool { + let mut should_quit = false; + self.input_devices.update(); + for event in self.event_pump.poll_iter() { + self.input_devices.handle_event(&event); + if event == SystemEvent::Quit { + should_quit = true; + } + } + should_quit + } - /// Convenience method that applies any [`AudioBuffer`]s that may have been queued up on - /// [`System::audio_queue`] to the system audio device so that they will be played. Do not - /// call this when you already have an active lock on an [`AudioDevice`]. - pub fn apply_audio_queue(&mut self) -> Result<(), AudioDeviceError> { - self.audio_queue.apply(&mut self.audio) - } + /// Convenience method that applies any [`AudioBuffer`]s that may have been queued up on + /// [`System::audio_queue`] to the system audio device so that they will be played. Do not + /// call this when you already have an active lock on an [`AudioDevice`]. + pub fn apply_audio_queue(&mut self) -> Result<(), AudioDeviceError> { + self.audio_queue.apply(&mut self.audio) + } - pub fn ticks(&self) -> u64 { - self.sdl_timer_subsystem.performance_counter() - } + pub fn ticks(&self) -> u64 { + self.sdl_timer_subsystem.performance_counter() + } - pub fn tick_frequency(&self) -> u64 { - self.sdl_timer_subsystem.performance_frequency() - } + pub fn tick_frequency(&self) -> u64 { + self.sdl_timer_subsystem.performance_frequency() + } - /// Returns the number of milliseconds elapsed since SDL was initialized. - pub fn millis(&self) -> u32 { - self.sdl_timer_subsystem.ticks() - } + /// Returns the number of milliseconds elapsed since SDL was initialized. + pub fn millis(&self) -> u32 { + self.sdl_timer_subsystem.ticks() + } - /// Delays (blocks) for about the number of milliseconds specified. - pub fn delay(&mut self, millis: u32) { - self.sdl_timer_subsystem.delay(millis); - } + /// Delays (blocks) for about the number of milliseconds specified. + pub fn delay(&mut self, millis: u32) { + self.sdl_timer_subsystem.delay(millis); + } } diff --git a/libretrogd/src/utils/bytes.rs b/libretrogd/src/utils/bytes.rs index 5e954db..2304366 100644 --- a/libretrogd/src/utils/bytes.rs +++ b/libretrogd/src/utils/bytes.rs @@ -1,12 +1,12 @@ pub trait ReadFixedLengthByteArray { - fn read_bytes(&mut self) -> Result<[u8; N], std::io::Error>; + fn read_bytes(&mut self) -> Result<[u8; N], std::io::Error>; } impl ReadFixedLengthByteArray for T { - fn read_bytes(&mut self) -> Result<[u8; N], std::io::Error> { - assert_ne!(N, 0); - let mut array = [0u8; N]; - self.read_exact(&mut array)?; - Ok(array) - } + fn read_bytes(&mut self) -> Result<[u8; N], std::io::Error> { + assert_ne!(N, 0); + let mut array = [0u8; N]; + self.read_exact(&mut array)?; + Ok(array) + } } diff --git a/libretrogd/src/utils/io.rs b/libretrogd/src/utils/io.rs index 3753469..f652b89 100644 --- a/libretrogd/src/utils/io.rs +++ b/libretrogd/src/utils/io.rs @@ -3,20 +3,20 @@ use std::io::{Error, SeekFrom}; /// Provides a convenience method for determining the total size of a stream. This is provided /// as a temporary alternative to [std::io::Seek::stream_len] which is currently marked unstable. pub trait StreamSize { - fn stream_size(&mut self) -> Result; + fn stream_size(&mut self) -> Result; } impl StreamSize for T { - fn stream_size(&mut self) -> Result { - let old_pos = self.stream_position()?; - let len = self.seek(SeekFrom::End(0))?; + fn stream_size(&mut self) -> Result { + let old_pos = self.stream_position()?; + let len = self.seek(SeekFrom::End(0))?; - // Avoid seeking a third time when we were already at the end of the - // stream. The branch is usually way cheaper than a seek operation. - if old_pos != len { - self.seek(SeekFrom::Start(old_pos))?; - } + // Avoid seeking a third time when we were already at the end of the + // stream. The branch is usually way cheaper than a seek operation. + if old_pos != len { + self.seek(SeekFrom::Start(old_pos))?; + } - Ok(len) - } + Ok(len) + } } \ No newline at end of file diff --git a/libretrogd/src/utils/lzwgif.rs b/libretrogd/src/utils/lzwgif.rs index 4013fd3..13b5b53 100644 --- a/libretrogd/src/utils/lzwgif.rs +++ b/libretrogd/src/utils/lzwgif.rs @@ -26,26 +26,26 @@ use thiserror::Error; #[derive(Error, Debug)] pub enum LzwBytePackingError { - #[error("Code size bits {0} is unsupported")] - UnsupportedCodeSizeBits(usize), + #[error("Code size bits {0} is unsupported")] + UnsupportedCodeSizeBits(usize), - #[error("Not enough bits available in the buffer to push new value in")] - NotEnoughBits, + #[error("Not enough bits available in the buffer to push new value in")] + NotEnoughBits, } #[derive(Error, Debug)] pub enum LzwError { - #[error("Code size bits {0} is unsupported")] - UnsupportedCodeSizeBits(usize), + #[error("Code size bits {0} is unsupported")] + UnsupportedCodeSizeBits(usize), - #[error("LZW byte packing/unpacking error")] - BytePackingError(#[from] LzwBytePackingError), + #[error("LZW byte packing/unpacking error")] + BytePackingError(#[from] LzwBytePackingError), - #[error("Encoding/decoding error: {0}")] - EncodingError(String), + #[error("Encoding/decoding error: {0}")] + EncodingError(String), - #[error("LZW I/O error")] - IOError(#[from] std::io::Error), + #[error("LZW I/O error")] + IOError(#[from] std::io::Error), } type LzwCode = u16; @@ -57,630 +57,630 @@ const MAX_BITS: usize = 12; const MAX_CODE_VALUE: LzwCode = (1 as LzwCode).wrapping_shl(MAX_BITS as u32) - 1; fn is_valid_code_size_bits(code_size_bits: usize) -> bool { - code_size_bits >= MIN_BITS && code_size_bits <= MAX_BITS + code_size_bits >= MIN_BITS && code_size_bits <= MAX_BITS } fn is_valid_gif_min_code_size_bits(min_code_size_bits: usize) -> bool { - min_code_size_bits >= MIN_BITS && min_code_size_bits <= GIF_MAX_CODE_SIZE_BITS + min_code_size_bits >= MIN_BITS && min_code_size_bits <= GIF_MAX_CODE_SIZE_BITS } fn get_bitmask_for_bits(bits: usize) -> u32 { - let mut bitmask = 0; - for i in 0..bits { - bitmask |= 1u32.wrapping_shl(i as u32); - } - bitmask + let mut bitmask = 0; + for i in 0..bits { + bitmask |= 1u32.wrapping_shl(i as u32); + } + bitmask } fn get_table_size_for_bits(bits: usize) -> usize { - 1usize.wrapping_shl(bits as u32) + 1usize.wrapping_shl(bits as u32) } fn get_max_code_value_for_bits(bits: usize) -> LzwCode { - (1 as LzwCode).wrapping_shl(bits as u32) - 1 + (1 as LzwCode).wrapping_shl(bits as u32) - 1 } #[derive(Debug)] struct LzwBytePacker { - buffer: u32, - buffer_length: usize, - current_bit_size: usize, - bitmask: u32, - initial_bit_size: usize, + buffer: u32, + buffer_length: usize, + current_bit_size: usize, + bitmask: u32, + initial_bit_size: usize, } impl LzwBytePacker { - pub fn new(initial_bit_size: usize) -> Result { - if !is_valid_code_size_bits(initial_bit_size) { - return Err(LzwBytePackingError::UnsupportedCodeSizeBits(initial_bit_size)); - } + pub fn new(initial_bit_size: usize) -> Result { + if !is_valid_code_size_bits(initial_bit_size) { + return Err(LzwBytePackingError::UnsupportedCodeSizeBits(initial_bit_size)); + } - Ok(LzwBytePacker { - buffer: 0, - buffer_length: 0, - current_bit_size: initial_bit_size, - bitmask: get_bitmask_for_bits(initial_bit_size), - initial_bit_size, - }) - } + Ok(LzwBytePacker { + buffer: 0, + buffer_length: 0, + current_bit_size: initial_bit_size, + bitmask: get_bitmask_for_bits(initial_bit_size), + initial_bit_size, + }) + } - #[inline] - fn remaining_space(&self) -> usize { - 32 - self.buffer_length - } + #[inline] + fn remaining_space(&self) -> usize { + 32 - self.buffer_length + } - pub fn increase_bit_size(&mut self) -> Result { - if self.current_bit_size >= MAX_BITS { - return Err(LzwBytePackingError::UnsupportedCodeSizeBits(self.current_bit_size + 1)); - } else { - self.current_bit_size += 1; - self.bitmask = get_bitmask_for_bits(self.current_bit_size); - Ok(self.current_bit_size) - } - } + pub fn increase_bit_size(&mut self) -> Result { + if self.current_bit_size >= MAX_BITS { + return Err(LzwBytePackingError::UnsupportedCodeSizeBits(self.current_bit_size + 1)); + } else { + self.current_bit_size += 1; + self.bitmask = get_bitmask_for_bits(self.current_bit_size); + Ok(self.current_bit_size) + } + } - pub fn reset_bit_size(&mut self) { - self.current_bit_size = self.initial_bit_size; - self.bitmask = get_bitmask_for_bits(self.current_bit_size); - } + pub fn reset_bit_size(&mut self) { + self.current_bit_size = self.initial_bit_size; + self.bitmask = get_bitmask_for_bits(self.current_bit_size); + } - pub fn push_code(&mut self, code: LzwCode) -> Result<(), LzwBytePackingError> { - if self.remaining_space() >= self.current_bit_size { - let value = (code as u32 & self.bitmask).wrapping_shl(self.buffer_length as u32); - self.buffer |= value; - self.buffer_length += self.current_bit_size; - Ok(()) - } else { - Err(LzwBytePackingError::NotEnoughBits) - } - } + pub fn push_code(&mut self, code: LzwCode) -> Result<(), LzwBytePackingError> { + if self.remaining_space() >= self.current_bit_size { + let value = (code as u32 & self.bitmask).wrapping_shl(self.buffer_length as u32); + self.buffer |= value; + self.buffer_length += self.current_bit_size; + Ok(()) + } else { + Err(LzwBytePackingError::NotEnoughBits) + } + } - pub fn take_byte(&mut self) -> Option { - if self.buffer_length >= 8 { - let byte = (self.buffer & 0xff) as u8; - self.buffer = self.buffer.wrapping_shr(8); - self.buffer_length -= 8; - Some(byte) - } else { - None - } - } + pub fn take_byte(&mut self) -> Option { + if self.buffer_length >= 8 { + let byte = (self.buffer & 0xff) as u8; + self.buffer = self.buffer.wrapping_shr(8); + self.buffer_length -= 8; + Some(byte) + } else { + None + } + } - pub fn flush_byte(&mut self) -> Option { - if self.buffer_length > 0 { - let byte = (self.buffer & 0xff) as u8; - self.buffer = self.buffer.wrapping_shr(8); - if self.buffer_length >= 8 { - self.buffer_length -= 8; - } else { - self.buffer_length = 0; - } - Some(byte) - } else { - None - } - } + pub fn flush_byte(&mut self) -> Option { + if self.buffer_length > 0 { + let byte = (self.buffer & 0xff) as u8; + self.buffer = self.buffer.wrapping_shr(8); + if self.buffer_length >= 8 { + self.buffer_length -= 8; + } else { + self.buffer_length = 0; + } + Some(byte) + } else { + None + } + } } #[derive(Debug)] struct LzwBytesWriter { - packer: LzwBytePacker, - buffer: Vec, + packer: LzwBytePacker, + buffer: Vec, } impl LzwBytesWriter { - pub fn new(code_size_bits: usize) -> Result { - if !is_valid_code_size_bits(code_size_bits) { - return Err(LzwError::UnsupportedCodeSizeBits(code_size_bits)); - } + pub fn new(code_size_bits: usize) -> Result { + if !is_valid_code_size_bits(code_size_bits) { + return Err(LzwError::UnsupportedCodeSizeBits(code_size_bits)); + } - Ok(LzwBytesWriter { - packer: LzwBytePacker::new(code_size_bits)?, - buffer: Vec::with_capacity(GIF_MAX_SUB_CHUNK_SIZE), - }) - } + Ok(LzwBytesWriter { + packer: LzwBytePacker::new(code_size_bits)?, + buffer: Vec::with_capacity(GIF_MAX_SUB_CHUNK_SIZE), + }) + } - #[inline] - pub fn increase_bit_size(&mut self) -> Result { - Ok(self.packer.increase_bit_size()?) - } + #[inline] + pub fn increase_bit_size(&mut self) -> Result { + Ok(self.packer.increase_bit_size()?) + } - #[inline] - pub fn reset_bit_size(&mut self) { - self.packer.reset_bit_size() - } + #[inline] + pub fn reset_bit_size(&mut self) { + self.packer.reset_bit_size() + } - fn write_buffer(&mut self, writer: &mut T) -> Result<(), LzwError> { - if !self.buffer.is_empty() { - writer.write_u8(self.buffer.len() as u8)?; - writer.write_all(&self.buffer)?; - self.buffer.clear(); - } - Ok(()) - } + fn write_buffer(&mut self, writer: &mut T) -> Result<(), LzwError> { + if !self.buffer.is_empty() { + writer.write_u8(self.buffer.len() as u8)?; + writer.write_all(&self.buffer)?; + self.buffer.clear(); + } + Ok(()) + } - pub fn write_code( - &mut self, - writer: &mut T, - code: LzwCode - ) -> Result<(), LzwError> { - self.packer.push_code(code)?; + pub fn write_code( + &mut self, + writer: &mut T, + code: LzwCode, + ) -> Result<(), LzwError> { + self.packer.push_code(code)?; - while let Some(byte) = self.packer.take_byte() { - self.buffer.push(byte); - if self.buffer.len() == GIF_MAX_SUB_CHUNK_SIZE { - self.write_buffer(writer)?; - } - } + while let Some(byte) = self.packer.take_byte() { + self.buffer.push(byte); + if self.buffer.len() == GIF_MAX_SUB_CHUNK_SIZE { + self.write_buffer(writer)?; + } + } - Ok(()) - } + Ok(()) + } - pub fn flush(&mut self, writer: &mut T) -> Result<(), LzwError> { - while let Some(byte) = self.packer.flush_byte() { - self.buffer.push(byte); - if self.buffer.len() == GIF_MAX_SUB_CHUNK_SIZE { - self.write_buffer(writer)?; - } - } - self.write_buffer(writer)?; - // block terminator for data sub-block sequence - writer.write_u8(0)?; - Ok(()) - } + pub fn flush(&mut self, writer: &mut T) -> Result<(), LzwError> { + while let Some(byte) = self.packer.flush_byte() { + self.buffer.push(byte); + if self.buffer.len() == GIF_MAX_SUB_CHUNK_SIZE { + self.write_buffer(writer)?; + } + } + self.write_buffer(writer)?; + // block terminator for data sub-block sequence + writer.write_u8(0)?; + Ok(()) + } } #[derive(Debug)] struct LzwByteUnpacker { - buffer: u32, - buffer_length: usize, - current_bit_size: usize, - bitmask: u32, - initial_bit_size: usize, + buffer: u32, + buffer_length: usize, + current_bit_size: usize, + bitmask: u32, + initial_bit_size: usize, } impl LzwByteUnpacker { - pub fn new(initial_bit_size: usize) -> Result { - if !is_valid_code_size_bits(initial_bit_size) { - return Err(LzwBytePackingError::UnsupportedCodeSizeBits(initial_bit_size)); - } + pub fn new(initial_bit_size: usize) -> Result { + if !is_valid_code_size_bits(initial_bit_size) { + return Err(LzwBytePackingError::UnsupportedCodeSizeBits(initial_bit_size)); + } - Ok(LzwByteUnpacker { - buffer: 0, - buffer_length: 0, - current_bit_size: initial_bit_size, - bitmask: get_bitmask_for_bits(initial_bit_size), - initial_bit_size, - }) - } + Ok(LzwByteUnpacker { + buffer: 0, + buffer_length: 0, + current_bit_size: initial_bit_size, + bitmask: get_bitmask_for_bits(initial_bit_size), + initial_bit_size, + }) + } - #[inline] - fn remaining_space(&self) -> usize { - 32 - self.buffer_length - } + #[inline] + fn remaining_space(&self) -> usize { + 32 - self.buffer_length + } - pub fn increase_bit_size(&mut self) -> Result { - if self.current_bit_size >= MAX_BITS { - return Err(LzwBytePackingError::UnsupportedCodeSizeBits(self.current_bit_size + 1)); - } else { - self.current_bit_size += 1; - self.bitmask = get_bitmask_for_bits(self.current_bit_size); - Ok(self.current_bit_size) - } - } + pub fn increase_bit_size(&mut self) -> Result { + if self.current_bit_size >= MAX_BITS { + return Err(LzwBytePackingError::UnsupportedCodeSizeBits(self.current_bit_size + 1)); + } else { + self.current_bit_size += 1; + self.bitmask = get_bitmask_for_bits(self.current_bit_size); + Ok(self.current_bit_size) + } + } - pub fn reset_bit_size(&mut self) { - self.current_bit_size = self.initial_bit_size; - self.bitmask = get_bitmask_for_bits(self.current_bit_size); - } + pub fn reset_bit_size(&mut self) { + self.current_bit_size = self.initial_bit_size; + self.bitmask = get_bitmask_for_bits(self.current_bit_size); + } - pub fn push_byte(&mut self, byte: u8) -> Result<(), LzwBytePackingError> { - if self.remaining_space() >= 8 { - let value = (byte as u32).wrapping_shl(self.buffer_length as u32); - self.buffer |= value; - self.buffer_length += 8; - Ok(()) - } else { - Err(LzwBytePackingError::NotEnoughBits) - } - } + pub fn push_byte(&mut self, byte: u8) -> Result<(), LzwBytePackingError> { + if self.remaining_space() >= 8 { + let value = (byte as u32).wrapping_shl(self.buffer_length as u32); + self.buffer |= value; + self.buffer_length += 8; + Ok(()) + } else { + Err(LzwBytePackingError::NotEnoughBits) + } + } - pub fn take_code(&mut self) -> Option { - if self.buffer_length >= self.current_bit_size { - let code = (self.buffer & self.bitmask) as LzwCode; - self.buffer = self.buffer.wrapping_shr(self.current_bit_size as u32); - self.buffer_length -= self.current_bit_size; - Some(code) - } else { - None - } - } + pub fn take_code(&mut self) -> Option { + if self.buffer_length >= self.current_bit_size { + let code = (self.buffer & self.bitmask) as LzwCode; + self.buffer = self.buffer.wrapping_shr(self.current_bit_size as u32); + self.buffer_length -= self.current_bit_size; + Some(code) + } else { + None + } + } } #[derive(Debug)] struct LzwBytesReader { - unpacker: LzwByteUnpacker, - sub_chunk_remaining_bytes: u8, - reached_end: bool, + unpacker: LzwByteUnpacker, + sub_chunk_remaining_bytes: u8, + reached_end: bool, } impl LzwBytesReader { - pub fn new(code_size_bits: usize) -> Result { - if !is_valid_code_size_bits(code_size_bits) { - return Err(LzwError::UnsupportedCodeSizeBits(code_size_bits)); - } + pub fn new(code_size_bits: usize) -> Result { + if !is_valid_code_size_bits(code_size_bits) { + return Err(LzwError::UnsupportedCodeSizeBits(code_size_bits)); + } - Ok(LzwBytesReader { - unpacker: LzwByteUnpacker::new(code_size_bits)?, - sub_chunk_remaining_bytes: 0, - reached_end: false, - }) - } + Ok(LzwBytesReader { + unpacker: LzwByteUnpacker::new(code_size_bits)?, + sub_chunk_remaining_bytes: 0, + reached_end: false, + }) + } - #[inline] - pub fn increase_bit_size(&mut self) -> Result { - Ok(self.unpacker.increase_bit_size()?) - } + #[inline] + pub fn increase_bit_size(&mut self) -> Result { + Ok(self.unpacker.increase_bit_size()?) + } - pub fn reset_bit_size(&mut self) { - self.unpacker.reset_bit_size() - } + pub fn reset_bit_size(&mut self) { + self.unpacker.reset_bit_size() + } - fn read_byte(&mut self, reader: &mut T) -> Result, LzwError> { - if self.reached_end { - return Ok(None); - } - // if we reached the end of the current sub-chunk, read the length of the next sub-chunk. - // if that length is zero, then we're done reading all the sub-chunks in the series (as - // there should always be a terminator zero byte at the end of the sequence). - if self.sub_chunk_remaining_bytes == 0 { - self.sub_chunk_remaining_bytes = reader.read_u8()?; - if self.sub_chunk_remaining_bytes == 0 { - self.reached_end = true; - return Ok(None); - } - } + fn read_byte(&mut self, reader: &mut T) -> Result, LzwError> { + if self.reached_end { + return Ok(None); + } + // if we reached the end of the current sub-chunk, read the length of the next sub-chunk. + // if that length is zero, then we're done reading all the sub-chunks in the series (as + // there should always be a terminator zero byte at the end of the sequence). + if self.sub_chunk_remaining_bytes == 0 { + self.sub_chunk_remaining_bytes = reader.read_u8()?; + if self.sub_chunk_remaining_bytes == 0 { + self.reached_end = true; + return Ok(None); + } + } - self.sub_chunk_remaining_bytes -= 1; - Ok(Some(reader.read_u8()?)) - } + self.sub_chunk_remaining_bytes -= 1; + Ok(Some(reader.read_u8()?)) + } - pub fn read_code ( - &mut self, - reader: &mut T, - ) -> Result, LzwError> { - loop { - if let Some(code) = self.unpacker.take_code() { - return Ok(Some(code)) - } else { - match self.read_byte(reader) { - Ok(Some(byte)) => self.unpacker.push_byte(byte)?, - Ok(None) => return Ok(None), - Err(LzwError::IOError(error)) if error.kind() == std::io::ErrorKind::UnexpectedEof => { - return Ok(None) - }, - Err(error) => return Err(error), - }; - } - } - } + pub fn read_code( + &mut self, + reader: &mut T, + ) -> Result, LzwError> { + loop { + if let Some(code) = self.unpacker.take_code() { + return Ok(Some(code)); + } else { + match self.read_byte(reader) { + Ok(Some(byte)) => self.unpacker.push_byte(byte)?, + Ok(None) => return Ok(None), + Err(LzwError::IOError(error)) if error.kind() == std::io::ErrorKind::UnexpectedEof => { + return Ok(None); + } + Err(error) => return Err(error), + }; + } + } + } } /// Encodes data read from the `src` using LZW (GIF-variant) compression, writing the encoded /// data out to `dest`. The LZW minimum code bit size is specified via `min_code_size`. pub fn lzw_encode( - src: &mut S, - dest: &mut D, - min_code_size: usize + src: &mut S, + dest: &mut D, + min_code_size: usize, ) -> Result<(), LzwError> -where - S: ReadBytesExt, - D: WriteBytesExt + where + S: ReadBytesExt, + D: WriteBytesExt { - if !is_valid_gif_min_code_size_bits(min_code_size) { - return Err(LzwError::UnsupportedCodeSizeBits(min_code_size)); - } + if !is_valid_gif_min_code_size_bits(min_code_size) { + return Err(LzwError::UnsupportedCodeSizeBits(min_code_size)); + } - // initialize the table, special codes, bit size info, etc - // note that we do not add clear_code or end_of_info_code to the table since they aren't really - // needed in the table (they are never looked up in it). this also saves us the trouble of - // needing the table to be able to hold buffers containing u16's instead of just u8's. - // this does mean that the size of the table is always 2 less than the number of created codes. + // initialize the table, special codes, bit size info, etc + // note that we do not add clear_code or end_of_info_code to the table since they aren't really + // needed in the table (they are never looked up in it). this also saves us the trouble of + // needing the table to be able to hold buffers containing u16's instead of just u8's. + // this does mean that the size of the table is always 2 less than the number of created codes. - let initial_table_size = get_table_size_for_bits(min_code_size); - let clear_code = initial_table_size as LzwCode; - let end_of_info_code = initial_table_size as LzwCode + 1; - let mut table = HashMap::, LzwCode>::with_capacity(initial_table_size + 2); - for i in 0..initial_table_size { - table.insert(vec![i as u8], i as LzwCode); - } - let mut current_bit_size = min_code_size + 1; - let mut max_code_value_for_bit_size = get_max_code_value_for_bits(current_bit_size); - let mut next_code = initial_table_size as LzwCode + 2; + let initial_table_size = get_table_size_for_bits(min_code_size); + let clear_code = initial_table_size as LzwCode; + let end_of_info_code = initial_table_size as LzwCode + 1; + let mut table = HashMap::, LzwCode>::with_capacity(initial_table_size + 2); + for i in 0..initial_table_size { + table.insert(vec![i as u8], i as LzwCode); + } + let mut current_bit_size = min_code_size + 1; + let mut max_code_value_for_bit_size = get_max_code_value_for_bits(current_bit_size); + let mut next_code = initial_table_size as LzwCode + 2; - // begin the output code stream. always write the min_code_size first as a normal byte. then - // write out the clear_code, being sure to encode it using our normal dynamic bit sizing - dest.write_u8(min_code_size as u8)?; - let mut writer = LzwBytesWriter::new(current_bit_size)?; - writer.write_code(dest, clear_code)?; + // begin the output code stream. always write the min_code_size first as a normal byte. then + // write out the clear_code, being sure to encode it using our normal dynamic bit sizing + dest.write_u8(min_code_size as u8)?; + let mut writer = LzwBytesWriter::new(current_bit_size)?; + writer.write_code(dest, clear_code)?; - // read first byte to start things off before the main loop. - // if we eof here for some reason, just end the lzw stream like "normal" ... even though this - // isn't really a normal situation - let byte = match src.read_u8() { - Ok(byte) => byte, - Err(ref error) if error.kind() == std::io::ErrorKind::UnexpectedEof => { - writer.write_code(dest, end_of_info_code)?; - writer.flush(dest)?; - return Ok(()); - }, - Err(error) => return Err(LzwError::IOError(error)) - }; + // read first byte to start things off before the main loop. + // if we eof here for some reason, just end the lzw stream like "normal" ... even though this + // isn't really a normal situation + let byte = match src.read_u8() { + Ok(byte) => byte, + Err(ref error) if error.kind() == std::io::ErrorKind::UnexpectedEof => { + writer.write_code(dest, end_of_info_code)?; + writer.flush(dest)?; + return Ok(()); + } + Err(error) => return Err(LzwError::IOError(error)) + }; - let mut buffer = vec![byte]; + let mut buffer = vec![byte]; - loop { - // grab the next byte - let byte = match src.read_u8() { - Ok(byte) => byte, - Err(ref error) if error.kind() == std::io::ErrorKind::UnexpectedEof => break, - Err(error) => return Err(LzwError::IOError(error)) - }; + loop { + // grab the next byte + let byte = match src.read_u8() { + Ok(byte) => byte, + Err(ref error) if error.kind() == std::io::ErrorKind::UnexpectedEof => break, + Err(error) => return Err(LzwError::IOError(error)) + }; - // check if the table currently contains a string composed of the current buffer plus - // the byte we just read ( - let mut buffer_plus_byte = buffer.clone(); - buffer_plus_byte.push(byte); + // check if the table currently contains a string composed of the current buffer plus + // the byte we just read ( + let mut buffer_plus_byte = buffer.clone(); + buffer_plus_byte.push(byte); - if table.contains_key(&buffer_plus_byte) { - // we have a match, so lets just keep collecting bytes in our buffer ... - buffer.push(byte); - } else { - // no match in the table, so we need to create a new code in the table for this - // string of bytes (buffer + byte) and also emit the code for _just_ the buffer string + if table.contains_key(&buffer_plus_byte) { + // we have a match, so lets just keep collecting bytes in our buffer ... + buffer.push(byte); + } else { + // no match in the table, so we need to create a new code in the table for this + // string of bytes (buffer + byte) and also emit the code for _just_ the buffer string - let new_code = next_code; - next_code += 1; + let new_code = next_code; + next_code += 1; - table.insert(buffer_plus_byte, new_code); + table.insert(buffer_plus_byte, new_code); - if let Some(code) = table.get(&buffer) { - writer.write_code(dest, *code)?; - } else { - return Err(LzwError::EncodingError(format!("Expected to find code in table for buffer {:?} but none was found", buffer))); - } + if let Some(code) = table.get(&buffer) { + writer.write_code(dest, *code)?; + } else { + return Err(LzwError::EncodingError(format!("Expected to find code in table for buffer {:?} but none was found", buffer))); + } - // bump up to the next bit size once we've seen enough codes to necessitate it ... - // note that this just means codes that exist in the table, not _necessarily_ codes - // which have actually been written out yet ... - if new_code > max_code_value_for_bit_size { - current_bit_size += 1; - max_code_value_for_bit_size = get_max_code_value_for_bits(current_bit_size); - writer.increase_bit_size()?; - } + // bump up to the next bit size once we've seen enough codes to necessitate it ... + // note that this just means codes that exist in the table, not _necessarily_ codes + // which have actually been written out yet ... + if new_code > max_code_value_for_bit_size { + current_bit_size += 1; + max_code_value_for_bit_size = get_max_code_value_for_bits(current_bit_size); + writer.increase_bit_size()?; + } - // reset the table and code bit sizes once we've seen enough codes to fill all our - // allowed bits. again, this is just based on codes that exist in the table! - if new_code == MAX_CODE_VALUE { - // we reached the maximum code bit size, time to re-initialize the code table - table = HashMap::with_capacity(initial_table_size + 2); - for i in 0..initial_table_size { - table.insert(vec![i as u8], i as LzwCode); - } - current_bit_size = min_code_size + 1; - max_code_value_for_bit_size = get_max_code_value_for_bits(current_bit_size); - next_code = initial_table_size as LzwCode + 2; + // reset the table and code bit sizes once we've seen enough codes to fill all our + // allowed bits. again, this is just based on codes that exist in the table! + if new_code == MAX_CODE_VALUE { + // we reached the maximum code bit size, time to re-initialize the code table + table = HashMap::with_capacity(initial_table_size + 2); + for i in 0..initial_table_size { + table.insert(vec![i as u8], i as LzwCode); + } + current_bit_size = min_code_size + 1; + max_code_value_for_bit_size = get_max_code_value_for_bits(current_bit_size); + next_code = initial_table_size as LzwCode + 2; - // reset the output code stream - writer.write_code(dest, clear_code)?; - writer.reset_bit_size(); - } + // reset the output code stream + writer.write_code(dest, clear_code)?; + writer.reset_bit_size(); + } - buffer = vec![byte]; - } - } + buffer = vec![byte]; + } + } - // flush the remaining buffer and finish up the output code stream + // flush the remaining buffer and finish up the output code stream - if let Some(code) = table.get(&buffer) { - writer.write_code(dest, *code)?; - } else { - return Err(LzwError::EncodingError(format!("No matching code for buffer {:?} at end of input stream", buffer))); - } + if let Some(code) = table.get(&buffer) { + writer.write_code(dest, *code)?; + } else { + return Err(LzwError::EncodingError(format!("No matching code for buffer {:?} at end of input stream", buffer))); + } - writer.write_code(dest, end_of_info_code)?; - writer.flush(dest)?; + writer.write_code(dest, end_of_info_code)?; + writer.flush(dest)?; - Ok(()) + Ok(()) } /// Decodes data read from the `src` using LZW (GIF-variant) decompression, writing the decoded /// data out to `dest`. pub fn lzw_decode( - src: &mut S, - dest: &mut D, + src: &mut S, + dest: &mut D, ) -> Result<(), LzwError> -where - S: ReadBytesExt, - D: WriteBytesExt + where + S: ReadBytesExt, + D: WriteBytesExt { - let min_code_size = src.read_u8()? as usize; + let min_code_size = src.read_u8()? as usize; - if !is_valid_gif_min_code_size_bits(min_code_size) { - return Err(LzwError::UnsupportedCodeSizeBits(min_code_size)); - } + if !is_valid_gif_min_code_size_bits(min_code_size) { + return Err(LzwError::UnsupportedCodeSizeBits(min_code_size)); + } - // initialize some basic properties for decoding and the table here ... we initialize the - // actual table and the rest of the decoding properties we need a bit further on below + // initialize some basic properties for decoding and the table here ... we initialize the + // actual table and the rest of the decoding properties we need a bit further on below - let mut current_bit_size = min_code_size + 1; - let initial_table_size = get_table_size_for_bits(min_code_size); - let clear_code = initial_table_size as LzwCode; - let end_of_info_code = initial_table_size as LzwCode + 1; + let mut current_bit_size = min_code_size + 1; + let initial_table_size = get_table_size_for_bits(min_code_size); + let clear_code = initial_table_size as LzwCode; + let end_of_info_code = initial_table_size as LzwCode + 1; - let mut reader = LzwBytesReader::new(current_bit_size)?; + let mut reader = LzwBytesReader::new(current_bit_size)?; - // read the first code from the input code stream. - // we also return immediately without writing anything to the destination byte stream if there - // are no codes to read (kind of a weird situation, but no real reason to error ...?) - let mut code = match reader.read_code(src)? { - Some(code) => code, - None => return Ok(()) - }; + // read the first code from the input code stream. + // we also return immediately without writing anything to the destination byte stream if there + // are no codes to read (kind of a weird situation, but no real reason to error ...?) + let mut code = match reader.read_code(src)? { + Some(code) => code, + None => return Ok(()) + }; - // the first code in the stream SHOULD be a clear code ... which we can just ignore because - // our table is freshly reset right now anyway. but we should flag this as an error if for some - // reason we didn't just read a clear code! - if code != clear_code { - return Err(LzwError::EncodingError(String::from("Unexpected first code value (not a clear code)"))); - } + // the first code in the stream SHOULD be a clear code ... which we can just ignore because + // our table is freshly reset right now anyway. but we should flag this as an error if for some + // reason we didn't just read a clear code! + if code != clear_code { + return Err(LzwError::EncodingError(String::from("Unexpected first code value (not a clear code)"))); + } - 'outer: loop { - // initialize the table and some extra bits of info here so that whenever we read in a - // clear code from the input stream, we can just loop back here to handle it + 'outer: loop { + // initialize the table and some extra bits of info here so that whenever we read in a + // clear code from the input stream, we can just loop back here to handle it - // note that we do not add clear_code or end_of_info_code to the table since they aren't really - // needed in the table (they are never looked up in it). this also saves us the trouble of - // needing the table to be able to hold buffers containing u16's instead of just u8's. - // this does mean that the size of the table is always 2 less than the number of created codes. + // note that we do not add clear_code or end_of_info_code to the table since they aren't really + // needed in the table (they are never looked up in it). this also saves us the trouble of + // needing the table to be able to hold buffers containing u16's instead of just u8's. + // this does mean that the size of the table is always 2 less than the number of created codes. - let mut table = vec![None; 1usize.wrapping_shl(MAX_BITS as u32)]; - for i in 0..initial_table_size { - table[i] = Some(vec![i as u8]); - } - let mut max_code_value_for_bit_size = get_max_code_value_for_bits(current_bit_size); - let mut next_code = initial_table_size as LzwCode + 2; + let mut table = vec![None; 1usize.wrapping_shl(MAX_BITS as u32)]; + for i in 0..initial_table_size { + table[i] = Some(vec![i as u8]); + } + let mut max_code_value_for_bit_size = get_max_code_value_for_bits(current_bit_size); + let mut next_code = initial_table_size as LzwCode + 2; - // read the next code which should actually be the first "interesting" value of the code stream - code = match reader.read_code(src)? { - Some(code) if code > MAX_CODE_VALUE => return Err(LzwError::EncodingError(format!("Encountered code that is too large: {}", code))), - Some(code) if code == end_of_info_code => return Ok(()), - Some(code) => code, - None => return Err(LzwError::EncodingError(String::from("Unexpected end of code stream"))), - }; + // read the next code which should actually be the first "interesting" value of the code stream + code = match reader.read_code(src)? { + Some(code) if code > MAX_CODE_VALUE => return Err(LzwError::EncodingError(format!("Encountered code that is too large: {}", code))), + Some(code) if code == end_of_info_code => return Ok(()), + Some(code) => code, + None => return Err(LzwError::EncodingError(String::from("Unexpected end of code stream"))), + }; - // ok, now we're able to get started! + // ok, now we're able to get started! - // simply write out the table string associated with the first code - if let Some(string) = table.get(code as usize).unwrap() { - dest.write_all(string)?; - } else { - return Err(LzwError::EncodingError(format!("No table entry for code {}", code))); - } + // simply write out the table string associated with the first code + if let Some(string) = table.get(code as usize).unwrap() { + dest.write_all(string)?; + } else { + return Err(LzwError::EncodingError(format!("No table entry for code {}", code))); + } - let mut prev_code = code; + let mut prev_code = code; - 'inner: loop { - // grab the next code - code = match reader.read_code(src)? { - Some(code) if code > MAX_CODE_VALUE => return Err(LzwError::EncodingError(format!("Encountered code that is too large: {}", code))), - Some(code) if code == end_of_info_code => break 'outer, - Some(code) if code == clear_code => { - // reset the bit size and reader and then loop back to the outer loop which - // will handle actually resetting the code table - current_bit_size = min_code_size + 1; - reader.reset_bit_size(); - break 'inner; - }, - Some(code) => code, - None => return Err(LzwError::EncodingError(String::from("Unexpected end of code stream"))), - }; + 'inner: loop { + // grab the next code + code = match reader.read_code(src)? { + Some(code) if code > MAX_CODE_VALUE => return Err(LzwError::EncodingError(format!("Encountered code that is too large: {}", code))), + Some(code) if code == end_of_info_code => break 'outer, + Some(code) if code == clear_code => { + // reset the bit size and reader and then loop back to the outer loop which + // will handle actually resetting the code table + current_bit_size = min_code_size + 1; + reader.reset_bit_size(); + break 'inner; + } + Some(code) => code, + None => return Err(LzwError::EncodingError(String::from("Unexpected end of code stream"))), + }; - // note: prev_code should always be present since we looked it up in the table during a - // previous loop iteration ... - let prev_code_string = match table.get(prev_code as usize).unwrap() { - Some(prev_code_string) => prev_code_string, - None => { - return Err(LzwError::EncodingError(format!("Previous code {} not found in table", prev_code))); - } - }; + // note: prev_code should always be present since we looked it up in the table during a + // previous loop iteration ... + let prev_code_string = match table.get(prev_code as usize).unwrap() { + Some(prev_code_string) => prev_code_string, + None => { + return Err(LzwError::EncodingError(format!("Previous code {} not found in table", prev_code))); + } + }; - let new_code = next_code; - next_code += 1; + let new_code = next_code; + next_code += 1; - if let Some(string) = table.get(code as usize).unwrap() { - // write out the matching table string for the code just read - dest.write_all(string)?; + if let Some(string) = table.get(code as usize).unwrap() { + // write out the matching table string for the code just read + dest.write_all(string)?; - // update the table accordingly - let k = string.first().unwrap(); - let mut new_string = prev_code_string.clone(); - new_string.push(*k); - table[new_code as usize] = Some(new_string); - } else { - // code is not yet present in the table. - // add prev_code string + the code we just read to the table and also write it out - let k = prev_code_string.first().unwrap(); - let mut new_string = prev_code_string.clone(); - new_string.push(*k); - dest.write_all(&new_string)?; - table[new_code as usize] = Some(new_string); - } + // update the table accordingly + let k = string.first().unwrap(); + let mut new_string = prev_code_string.clone(); + new_string.push(*k); + table[new_code as usize] = Some(new_string); + } else { + // code is not yet present in the table. + // add prev_code string + the code we just read to the table and also write it out + let k = prev_code_string.first().unwrap(); + let mut new_string = prev_code_string.clone(); + new_string.push(*k); + dest.write_all(&new_string)?; + table[new_code as usize] = Some(new_string); + } - if new_code == max_code_value_for_bit_size { - current_bit_size += 1; - max_code_value_for_bit_size = get_max_code_value_for_bits(current_bit_size); - reader.increase_bit_size()?; - } + if new_code == max_code_value_for_bit_size { + current_bit_size += 1; + max_code_value_for_bit_size = get_max_code_value_for_bits(current_bit_size); + reader.increase_bit_size()?; + } - prev_code = code; - } - } + prev_code = code; + } + } - Ok(()) + Ok(()) } #[cfg(test)] mod tests { - use std::io::Cursor; + use std::io::Cursor; - use super::*; + use super::*; - struct LzwTestData<'a> { - min_code_size: usize, - packed: &'a [u8], - unpacked: &'a [u8], - } + struct LzwTestData<'a> { + min_code_size: usize, + packed: &'a [u8], + unpacked: &'a [u8], + } - static LZW_TEST_DATA: &[LzwTestData] = &[ - LzwTestData { - min_code_size: 2, - packed: &[0x02, 0x16, 0x8c, 0x2d, 0x99, 0x87, 0x2a, 0x1c, 0xdc, 0x33, 0xa0, 0x02, 0x75, 0xec, 0x95, 0xfa, 0xa8, 0xde, 0x60, 0x8c, 0x04, 0x91, 0x4c, 0x01, 0x00], - unpacked: &[1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 1, 0, 0, 0, 0, 2, 2, 2, 1, 1, 1, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1], - }, - LzwTestData { - min_code_size: 4, - packed: &[0x04, 0x21, 0x70, 0x49, 0x79, 0x6a, 0x9d, 0xcb, 0x39, 0x7b, 0xa6, 0xd6, 0x96, 0xa4, 0x3d, 0x0f, 0xd8, 0x8d, 0x64, 0xb9, 0x1d, 0x28, 0xa9, 0x2d, 0x15, 0xfa, 0xc2, 0xf1, 0x37, 0x71, 0x33, 0xc5, 0x61, 0x4b, 0x04, 0x00], - unpacked: &[11, 11, 11, 11, 11, 7, 7, 7, 7, 7, 11, 11, 11, 11, 14, 14, 7, 7, 7, 7, 11, 11, 11, 14, 14, 14, 14, 7, 7, 7, 11, 11, 14, 14, 15, 15, 14, 14, 7, 7, 11, 14, 14, 15, 15, 15, 15, 14, 14, 7, 7, 14, 14, 15, 15, 15, 15, 14, 14, 11, 7, 7, 14, 14, 15, 15, 14, 14, 11, 11, 7, 7, 7, 14, 14, 14, 14, 11, 11, 11, 7, 7, 7, 7, 14, 14, 11, 11, 11, 11, 7, 7, 7, 7, 7, 11, 11, 11, 11, 11], - }, - LzwTestData { - min_code_size: 8, - packed: &[0x08, 0x0b, 0x00, 0x51, 0xfc, 0x1b, 0x28, 0x70, 0xa0, 0xc1, 0x83, 0x01, 0x01, 0x00], - unpacked: &[0x28, 0xff, 0xff, 0xff, 0x28, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], - } - ]; + static LZW_TEST_DATA: &[LzwTestData] = &[ + LzwTestData { + min_code_size: 2, + packed: &[0x02, 0x16, 0x8c, 0x2d, 0x99, 0x87, 0x2a, 0x1c, 0xdc, 0x33, 0xa0, 0x02, 0x75, 0xec, 0x95, 0xfa, 0xa8, 0xde, 0x60, 0x8c, 0x04, 0x91, 0x4c, 0x01, 0x00], + unpacked: &[1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 1, 0, 0, 0, 0, 2, 2, 2, 1, 1, 1, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1], + }, + LzwTestData { + min_code_size: 4, + packed: &[0x04, 0x21, 0x70, 0x49, 0x79, 0x6a, 0x9d, 0xcb, 0x39, 0x7b, 0xa6, 0xd6, 0x96, 0xa4, 0x3d, 0x0f, 0xd8, 0x8d, 0x64, 0xb9, 0x1d, 0x28, 0xa9, 0x2d, 0x15, 0xfa, 0xc2, 0xf1, 0x37, 0x71, 0x33, 0xc5, 0x61, 0x4b, 0x04, 0x00], + unpacked: &[11, 11, 11, 11, 11, 7, 7, 7, 7, 7, 11, 11, 11, 11, 14, 14, 7, 7, 7, 7, 11, 11, 11, 14, 14, 14, 14, 7, 7, 7, 11, 11, 14, 14, 15, 15, 14, 14, 7, 7, 11, 14, 14, 15, 15, 15, 15, 14, 14, 7, 7, 14, 14, 15, 15, 15, 15, 14, 14, 11, 7, 7, 14, 14, 15, 15, 14, 14, 11, 11, 7, 7, 7, 14, 14, 14, 14, 11, 11, 11, 7, 7, 7, 7, 14, 14, 11, 11, 11, 11, 7, 7, 7, 7, 7, 11, 11, 11, 11, 11], + }, + LzwTestData { + min_code_size: 8, + packed: &[0x08, 0x0b, 0x00, 0x51, 0xfc, 0x1b, 0x28, 0x70, 0xa0, 0xc1, 0x83, 0x01, 0x01, 0x00], + unpacked: &[0x28, 0xff, 0xff, 0xff, 0x28, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], + } + ]; - #[test] - fn lzw_compresses() -> Result<(), LzwError> { - for LzwTestData { packed, unpacked, min_code_size } in LZW_TEST_DATA { - let mut src = Cursor::new(*unpacked); - let mut dest = vec![0u8; 0]; - lzw_encode(&mut src, &mut dest, *min_code_size)?; - assert_eq!(dest, *packed); - } + #[test] + fn lzw_compresses() -> Result<(), LzwError> { + for LzwTestData { packed, unpacked, min_code_size } in LZW_TEST_DATA { + let mut src = Cursor::new(*unpacked); + let mut dest = vec![0u8; 0]; + lzw_encode(&mut src, &mut dest, *min_code_size)?; + assert_eq!(dest, *packed); + } - Ok(()) - } + Ok(()) + } - #[test] - fn lzw_decompresses() -> Result<(), LzwError> { - for LzwTestData { packed, unpacked, min_code_size: _ } in LZW_TEST_DATA { - let mut src = Cursor::new(*packed); - let mut dest = vec![0u8; 0]; - lzw_decode(&mut src, &mut dest)?; - assert_eq!(dest, *unpacked); - } + #[test] + fn lzw_decompresses() -> Result<(), LzwError> { + for LzwTestData { packed, unpacked, min_code_size: _ } in LZW_TEST_DATA { + let mut src = Cursor::new(*packed); + let mut dest = vec![0u8; 0]; + lzw_decode(&mut src, &mut dest)?; + assert_eq!(dest, *unpacked); + } - Ok(()) - } + Ok(()) + } } diff --git a/libretrogd/src/utils/mod.rs b/libretrogd/src/utils/mod.rs index 6fbdb46..8e1255f 100644 --- a/libretrogd/src/utils/mod.rs +++ b/libretrogd/src/utils/mod.rs @@ -10,31 +10,31 @@ pub mod lzwgif; pub mod packbits; pub fn rnd_value(low: N, high: N) -> N { - rand::thread_rng().gen_range(low..=high) + rand::thread_rng().gen_range(low..=high) } /// Returns the absolute difference between two unsigned values. This is just here as a temporary /// alternative to the `abs_diff` method currently provided by Rust but that is marked unstable. #[inline] pub fn abs_diff(a: N, b: N) -> N { - if a < b { - b - a - } else { - a - b - } + if a < b { + b - a + } else { + a - b + } } pub trait AsAny { - fn as_any(&self) -> &dyn Any; - fn as_any_mut(&mut self) -> &mut dyn Any; + fn as_any(&self) -> &dyn Any; + fn as_any_mut(&mut self) -> &mut dyn Any; } impl AsAny for A { - fn as_any(&self) -> &dyn Any { - self as &dyn Any - } + fn as_any(&self) -> &dyn Any { + self as &dyn Any + } - fn as_any_mut(&mut self) -> &mut dyn Any { - self as &mut dyn Any - } + fn as_any_mut(&mut self) -> &mut dyn Any { + self as &mut dyn Any + } } \ No newline at end of file diff --git a/libretrogd/src/utils/packbits.rs b/libretrogd/src/utils/packbits.rs index 1141e05..c3c6f22 100644 --- a/libretrogd/src/utils/packbits.rs +++ b/libretrogd/src/utils/packbits.rs @@ -3,224 +3,224 @@ use thiserror::Error; #[derive(Error, Debug)] pub enum PackBitsError { - #[error("PackBits I/O error")] - IOError(#[from] std::io::Error), + #[error("PackBits I/O error")] + IOError(#[from] std::io::Error), } enum PackMode { - Dump, - Run, + Dump, + Run, } pub fn pack_bits(src: &mut S, dest: &mut D, src_length: usize) -> Result<(), PackBitsError> -where - S: ReadBytesExt, - D: WriteBytesExt, + where + S: ReadBytesExt, + D: WriteBytesExt, { - const MIN_RUN: usize = 3; - const MAX_RUN: usize = 128; - const MAX_BUFFER: usize = 128; + const MIN_RUN: usize = 3; + const MAX_RUN: usize = 128; + const MAX_BUFFER: usize = 128; - if src_length == 0 { - return Ok(()); - } - let mut bytes_left = src_length; + if src_length == 0 { + return Ok(()); + } + let mut bytes_left = src_length; - let mut buffer = [0u8; (MAX_RUN * 2)]; + let mut buffer = [0u8; (MAX_RUN * 2)]; - // read the first byte from the source, just to start things off before we get into the loop - buffer[0] = src.read_u8()?; - bytes_left -= 1; + // read the first byte from the source, just to start things off before we get into the loop + buffer[0] = src.read_u8()?; + bytes_left -= 1; - let mut mode = PackMode::Dump; - let mut run_end = 1; // 1 because we already read the first byte into the buffer - let mut run_start = 0; - let mut previous_byte = buffer[0]; + let mut mode = PackMode::Dump; + let mut run_end = 1; // 1 because we already read the first byte into the buffer + let mut run_start = 0; + let mut previous_byte = buffer[0]; - while bytes_left > 0 { - let byte = src.read_u8()?; + while bytes_left > 0 { + let byte = src.read_u8()?; - buffer[run_end] = byte; - run_end += 1; + buffer[run_end] = byte; + run_end += 1; - match mode { - // "dump" mode. keep collecting any bytes and write them as-is until we detect the - // start of a run of identical bytes - PackMode::Dump => { - if run_end > MAX_BUFFER { - // we need to flush the temp buffer to the destination - dest.write_u8((run_end - 2) as u8)?; - dest.write_all(&buffer[0..run_end])?; + match mode { + // "dump" mode. keep collecting any bytes and write them as-is until we detect the + // start of a run of identical bytes + PackMode::Dump => { + if run_end > MAX_BUFFER { + // we need to flush the temp buffer to the destination + dest.write_u8((run_end - 2) as u8)?; + dest.write_all(&buffer[0..run_end])?; - buffer[0] = byte; - run_end = 1; - run_start = 0; - } else if byte == previous_byte { - // detected the start of a run of identical bytes - if (run_end - run_start) >= MIN_RUN { - if run_start > 0 { - // we've found a run, flush the buffer we have currently so we can - // start tracking the length of this run - dest.write_u8((run_start - 1) as u8)?; - dest.write_all(&buffer[0..run_start])?; - } - mode = PackMode::Run; - } else if run_start == 0 { - mode = PackMode::Run; - } - } else { - run_start = run_end - 1; - } - } - // "run" mode. keep counting up bytes as long as they are identical to each other. when - // we find the end of a run, write out the run info and switch back to dump mode - PackMode::Run => { - // check for the end of a run of identical bytes - if (byte != previous_byte) || ((run_end - run_start) > MAX_RUN) { - // the identical byte run has ended, write it out to the destination - // (this is just two bytes, the count and the actual byte) - dest.write_i8(-((run_end - run_start - 2) as i8))?; - dest.write_u8(previous_byte)?; + buffer[0] = byte; + run_end = 1; + run_start = 0; + } else if byte == previous_byte { + // detected the start of a run of identical bytes + if (run_end - run_start) >= MIN_RUN { + if run_start > 0 { + // we've found a run, flush the buffer we have currently so we can + // start tracking the length of this run + dest.write_u8((run_start - 1) as u8)?; + dest.write_all(&buffer[0..run_start])?; + } + mode = PackMode::Run; + } else if run_start == 0 { + mode = PackMode::Run; + } + } else { + run_start = run_end - 1; + } + } + // "run" mode. keep counting up bytes as long as they are identical to each other. when + // we find the end of a run, write out the run info and switch back to dump mode + PackMode::Run => { + // check for the end of a run of identical bytes + if (byte != previous_byte) || ((run_end - run_start) > MAX_RUN) { + // the identical byte run has ended, write it out to the destination + // (this is just two bytes, the count and the actual byte) + dest.write_i8(-((run_end - run_start - 2) as i8))?; + dest.write_u8(previous_byte)?; - // clear the temp buffer for our switch back to "dump" mode - buffer[0] = byte; - run_end = 1; - run_start = 0; - mode = PackMode::Dump; - } - } - }; + // clear the temp buffer for our switch back to "dump" mode + buffer[0] = byte; + run_end = 1; + run_start = 0; + mode = PackMode::Dump; + } + } + }; - previous_byte = byte; - bytes_left -= 1; - } + previous_byte = byte; + bytes_left -= 1; + } - // the source bytes have all been read, but we still might have to flush the temp buffer - // out to the destination, or finish writing out a run of identical bytes that was at the very - // end of the source - match mode { - PackMode::Dump => { - dest.write_u8((run_end - 1) as u8)?; - dest.write_all(&buffer[0..run_end])?; - } - PackMode::Run => { - dest.write_i8(-((run_end - run_start - 1) as i8))?; - dest.write_u8(previous_byte)?; - } - }; + // the source bytes have all been read, but we still might have to flush the temp buffer + // out to the destination, or finish writing out a run of identical bytes that was at the very + // end of the source + match mode { + PackMode::Dump => { + dest.write_u8((run_end - 1) as u8)?; + dest.write_all(&buffer[0..run_end])?; + } + PackMode::Run => { + dest.write_i8(-((run_end - run_start - 1) as i8))?; + dest.write_u8(previous_byte)?; + } + }; - Ok(()) + Ok(()) } pub fn unpack_bits( - src: &mut S, - dest: &mut D, - unpacked_length: usize, + src: &mut S, + dest: &mut D, + unpacked_length: usize, ) -> Result<(), PackBitsError> -where - S: ReadBytesExt, - D: WriteBytesExt, + where + S: ReadBytesExt, + D: WriteBytesExt, { - let mut buffer = [0u8; 128]; - let mut bytes_written = 0; + let mut buffer = [0u8; 128]; + let mut bytes_written = 0; - while bytes_written < unpacked_length { - // read the next "code" byte that determines how to process the subsequent byte(s) - let byte = src.read_u8()?; + while bytes_written < unpacked_length { + // read the next "code" byte that determines how to process the subsequent byte(s) + let byte = src.read_u8()?; - if byte > 128 { - // 129-255 = repeat the next byte 257-n times - let run_length = (257 - byte as u32) as usize; + if byte > 128 { + // 129-255 = repeat the next byte 257-n times + let run_length = (257 - byte as u32) as usize; - // read the next byte from the source and repeat it the specified number of times - let byte = src.read_u8()?; - buffer.fill(byte); - dest.write_all(&buffer[0..run_length])?; - bytes_written += run_length; - } else if byte < 128 { - // 0-128 = copy next n-1 bytes from src to dest as-is - let run_length = (byte + 1) as usize; + // read the next byte from the source and repeat it the specified number of times + let byte = src.read_u8()?; + buffer.fill(byte); + dest.write_all(&buffer[0..run_length])?; + bytes_written += run_length; + } else if byte < 128 { + // 0-128 = copy next n-1 bytes from src to dest as-is + let run_length = (byte + 1) as usize; - src.read_exact(&mut buffer[0..run_length])?; - dest.write_all(&buffer[0..run_length])?; - bytes_written += run_length; - } + src.read_exact(&mut buffer[0..run_length])?; + dest.write_all(&buffer[0..run_length])?; + bytes_written += run_length; + } - // note that byte == 128 is a no-op (does it even appear in any files ???) - } + // note that byte == 128 is a no-op (does it even appear in any files ???) + } - Ok(()) + Ok(()) } #[cfg(test)] mod tests { - use std::io::Cursor; + use std::io::Cursor; - use super::*; + use super::*; - struct TestData<'a> { - packed: &'a [u8], - unpacked: &'a [u8], - } + struct TestData<'a> { + packed: &'a [u8], + unpacked: &'a [u8], + } - static TEST_DATA: &[TestData] = &[ - TestData { - packed: &[ - 0xfe, 0xaa, 0x02, 0x80, 0x00, 0x2a, 0xfd, 0xaa, 0x03, 0x80, 0x00, 0x2a, 0x22, 0xf7, - 0xaa, - ], - unpacked: &[ - 0xaa, 0xaa, 0xaa, 0x80, 0x00, 0x2a, 0xaa, 0xaa, 0xaa, 0xaa, 0x80, 0x00, 0x2a, 0x22, - 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, - ], - }, - TestData { - packed: &[0x00, 0xaa], - unpacked: &[0xaa], - }, - TestData { - packed: &[0xf9, 0xaa], - unpacked: &[0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa], - }, - TestData { - packed: &[0xf9, 0xaa, 0x00, 0xbb], - unpacked: &[0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xbb], - }, - TestData { - packed: &[0x07, 0xa0, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8], - unpacked: &[0xa0, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8], - }, - TestData { - packed: &[0x08, 0xa0, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa8], - unpacked: &[0xa0, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa8], - }, - TestData { - packed: &[0x06, 0xa0, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xfe, 0xa8], - unpacked: &[0xa0, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa8, 0xa8], - }, - ]; + static TEST_DATA: &[TestData] = &[ + TestData { + packed: &[ + 0xfe, 0xaa, 0x02, 0x80, 0x00, 0x2a, 0xfd, 0xaa, 0x03, 0x80, 0x00, 0x2a, 0x22, 0xf7, + 0xaa, + ], + unpacked: &[ + 0xaa, 0xaa, 0xaa, 0x80, 0x00, 0x2a, 0xaa, 0xaa, 0xaa, 0xaa, 0x80, 0x00, 0x2a, 0x22, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + ], + }, + TestData { + packed: &[0x00, 0xaa], + unpacked: &[0xaa], + }, + TestData { + packed: &[0xf9, 0xaa], + unpacked: &[0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa], + }, + TestData { + packed: &[0xf9, 0xaa, 0x00, 0xbb], + unpacked: &[0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xbb], + }, + TestData { + packed: &[0x07, 0xa0, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8], + unpacked: &[0xa0, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8], + }, + TestData { + packed: &[0x08, 0xa0, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa8], + unpacked: &[0xa0, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa8], + }, + TestData { + packed: &[0x06, 0xa0, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xfe, 0xa8], + unpacked: &[0xa0, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa8, 0xa8], + }, + ]; - #[test] - fn packs() -> Result<(), PackBitsError> { - for TestData { packed, unpacked } in TEST_DATA { - let mut src = Cursor::new(*unpacked); - let mut dest = vec![0u8; 0]; - pack_bits(&mut src, &mut dest, unpacked.len())?; - assert_eq!(dest, *packed); - } + #[test] + fn packs() -> Result<(), PackBitsError> { + for TestData { packed, unpacked } in TEST_DATA { + let mut src = Cursor::new(*unpacked); + let mut dest = vec![0u8; 0]; + pack_bits(&mut src, &mut dest, unpacked.len())?; + assert_eq!(dest, *packed); + } - Ok(()) - } + Ok(()) + } - #[test] - fn unpacks() -> Result<(), PackBitsError> { - for TestData { packed, unpacked } in TEST_DATA { - let mut src = Cursor::new(*packed); - let mut dest = vec![0u8; 0]; - unpack_bits(&mut src, &mut dest, unpacked.len())?; - assert_eq!(dest, *unpacked); - } + #[test] + fn unpacks() -> Result<(), PackBitsError> { + for TestData { packed, unpacked } in TEST_DATA { + let mut src = Cursor::new(*packed); + let mut dest = vec![0u8; 0]; + unpack_bits(&mut src, &mut dest, unpacked.len())?; + assert_eq!(dest, *unpacked); + } - Ok(()) - } + Ok(()) + } } diff --git a/libretrogd/tests/graphics.rs b/libretrogd/tests/graphics.rs index b965990..999a83c 100644 --- a/libretrogd/tests/graphics.rs +++ b/libretrogd/tests/graphics.rs @@ -5,1781 +5,1781 @@ use libretrogd::{SCREEN_HEIGHT, SCREEN_WIDTH}; use libretrogd::graphics::*; fn setup() -> (Bitmap, Palette) { - let palette = Palette::new_vga_palette().unwrap(); - let screen = Bitmap::new(SCREEN_WIDTH, SCREEN_HEIGHT).unwrap(); - (screen, palette) + let palette = Palette::new_vga_palette().unwrap(); + let screen = Bitmap::new(SCREEN_WIDTH, SCREEN_HEIGHT).unwrap(); + (screen, palette) } fn setup_for_blending() -> (Bitmap, Palette, BlendMap) { - let (texture, palette) = Bitmap::load_file(Path::new("test-assets/texture.lbm")).unwrap(); - let blend_map = BlendMap::load_from_file(Path::new("test-assets/test.blendmap")).unwrap(); - let mut screen = Bitmap::new(SCREEN_WIDTH, SCREEN_HEIGHT).unwrap(); - for y in 0..(SCREEN_HEIGHT as f32 / texture.height() as f32).ceil() as i32 { - for x in 0..(SCREEN_WIDTH as f32 / texture.width() as f32).ceil() as i32 { - screen.blit(BlitMethod::Solid, &texture, x * texture.width() as i32, y * texture.height() as i32); - } - } - (screen, palette, blend_map) + let (texture, palette) = Bitmap::load_file(Path::new("test-assets/texture.lbm")).unwrap(); + let blend_map = BlendMap::load_from_file(Path::new("test-assets/test.blendmap")).unwrap(); + let mut screen = Bitmap::new(SCREEN_WIDTH, SCREEN_HEIGHT).unwrap(); + for y in 0..(SCREEN_HEIGHT as f32 / texture.height() as f32).ceil() as i32 { + for x in 0..(SCREEN_WIDTH as f32 / texture.width() as f32).ceil() as i32 { + screen.blit(BlitMethod::Solid, &texture, x * texture.width() as i32, y * texture.height() as i32); + } + } + (screen, palette, blend_map) } fn verify_visual(screen: &Bitmap, palette: &Palette, source: &Path) -> bool { - let (source_bmp, source_pal) = Bitmap::load_file(source).unwrap(); - *screen == source_bmp && *palette == source_pal + let (source_bmp, source_pal) = Bitmap::load_file(source).unwrap(); + *screen == source_bmp && *palette == source_pal } #[test] fn pixel_addressing() { - let (mut screen, palette) = setup(); + let (mut screen, palette) = setup(); - unsafe { - let mut pixels = screen.pixels_at_mut_ptr(10, 10).unwrap(); - let mut i = 0; - for _y in 0..16 { - for _x in 0..16 { - *pixels = i; - i = i.wrapping_add(1); - pixels = pixels.offset(1); - } - pixels = pixels.offset((SCREEN_WIDTH - 16) as isize); - } - } + unsafe { + let mut pixels = screen.pixels_at_mut_ptr(10, 10).unwrap(); + let mut i = 0; + for _y in 0..16 { + for _x in 0..16 { + *pixels = i; + i = i.wrapping_add(1); + pixels = pixels.offset(1); + } + pixels = pixels.offset((SCREEN_WIDTH - 16) as isize); + } + } - unsafe { - let mut pixels = screen.pixels_at_mut_ptr(0, 0).unwrap(); - for _ in 0..10 { - *pixels = 15; - pixels = pixels.offset((SCREEN_WIDTH + 1) as isize); - } - } + unsafe { + let mut pixels = screen.pixels_at_mut_ptr(0, 0).unwrap(); + for _ in 0..10 { + *pixels = 15; + pixels = pixels.offset((SCREEN_WIDTH + 1) as isize); + } + } - unsafe { - let mut pixels = screen.pixels_at_mut_ptr(10, 0).unwrap(); - for _ in 0..10 { - *pixels = 15; - pixels = pixels.offset(SCREEN_WIDTH as isize); - } - } + unsafe { + let mut pixels = screen.pixels_at_mut_ptr(10, 0).unwrap(); + for _ in 0..10 { + *pixels = 15; + pixels = pixels.offset(SCREEN_WIDTH as isize); + } + } - unsafe { - let mut pixels = screen.pixels_at_mut_ptr(0, 10).unwrap(); - for _ in 0..10 { - *pixels = 15; - pixels = pixels.offset(1); - } - } + unsafe { + let mut pixels = screen.pixels_at_mut_ptr(0, 10).unwrap(); + for _ in 0..10 { + *pixels = 15; + pixels = pixels.offset(1); + } + } - let path = Path::new("tests/ref/pixel_addressing.pcx"); - //screen.to_pcx_file(path, &palette).unwrap(); - assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); + let path = Path::new("tests/ref/pixel_addressing.pcx"); + //screen.to_pcx_file(path, &palette).unwrap(); + assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); } #[test] fn pixel_drawing() { - let (mut screen, palette) = setup(); + let (mut screen, palette) = setup(); - screen.set_pixel(0, 0, 1); - screen.set_pixel(319, 0, 2); - screen.set_pixel(0, 239, 3); - screen.set_pixel(319, 239, 4); + screen.set_pixel(0, 0, 1); + screen.set_pixel(319, 0, 2); + screen.set_pixel(0, 239, 3); + screen.set_pixel(319, 239, 4); - unsafe { - screen.set_pixel_unchecked(10, 0, 1); - screen.set_pixel_unchecked(309, 0, 2); - screen.set_pixel_unchecked(10, 239, 3); - screen.set_pixel_unchecked(309, 239, 4); - } + unsafe { + screen.set_pixel_unchecked(10, 0, 1); + screen.set_pixel_unchecked(309, 0, 2); + screen.set_pixel_unchecked(10, 239, 3); + screen.set_pixel_unchecked(309, 239, 4); + } - let c1 = screen.get_pixel(0, 0).unwrap(); - let c2 = screen.get_pixel(319, 0).unwrap(); - let c3 = screen.get_pixel(0, 239).unwrap(); - let c4 = screen.get_pixel(319, 239).unwrap(); + let c1 = screen.get_pixel(0, 0).unwrap(); + let c2 = screen.get_pixel(319, 0).unwrap(); + let c3 = screen.get_pixel(0, 239).unwrap(); + let c4 = screen.get_pixel(319, 239).unwrap(); - screen.set_pixel(1, 1, c1); - screen.set_pixel(318, 1, c2); - screen.set_pixel(1, 238, c3); - screen.set_pixel(318, 238, c4); + screen.set_pixel(1, 1, c1); + screen.set_pixel(318, 1, c2); + screen.set_pixel(1, 238, c3); + screen.set_pixel(318, 238, c4); - unsafe { - let c1 = screen.get_pixel_unchecked(10, 0); - let c2 = screen.get_pixel_unchecked(309, 0); - let c3 = screen.get_pixel_unchecked(10, 239); - let c4 = screen.get_pixel_unchecked(309, 239); + unsafe { + let c1 = screen.get_pixel_unchecked(10, 0); + let c2 = screen.get_pixel_unchecked(309, 0); + let c3 = screen.get_pixel_unchecked(10, 239); + let c4 = screen.get_pixel_unchecked(309, 239); - screen.set_pixel_unchecked(11, 1, c1); - screen.set_pixel_unchecked(308, 1, c2); - screen.set_pixel_unchecked(11, 238, c3); - screen.set_pixel_unchecked(308, 238, c4); - } + screen.set_pixel_unchecked(11, 1, c1); + screen.set_pixel_unchecked(308, 1, c2); + screen.set_pixel_unchecked(11, 238, c3); + screen.set_pixel_unchecked(308, 238, c4); + } - ////// + ////// - for i in 0..10 { - screen.set_pixel(5-i, 100, 15); - screen.set_pixel(i+314, 100, 15); - screen.set_pixel(160, 5-i, 15); - screen.set_pixel(160, i+234, 15); - } + for i in 0..10 { + screen.set_pixel(5 - i, 100, 15); + screen.set_pixel(i + 314, 100, 15); + screen.set_pixel(160, 5 - i, 15); + screen.set_pixel(160, i + 234, 15); + } - let path = Path::new("tests/ref/pixel_drawing.pcx"); - //screen.to_pcx_file(path, &palette).unwrap(); - assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); + let path = Path::new("tests/ref/pixel_drawing.pcx"); + //screen.to_pcx_file(path, &palette).unwrap(); + assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); } #[test] fn blended_pixel_drawing() { - let (mut screen, palette, blend_map) = setup_for_blending(); + let (mut screen, palette, blend_map) = setup_for_blending(); - for i in 0..10 { - screen.set_blended_pixel(0+i, 0+i, 1, &blend_map); - screen.set_blended_pixel(319-i, 0+i, 2, &blend_map); - screen.set_blended_pixel(0+i, 239-i, 3, &blend_map); - screen.set_blended_pixel(319-i, 239-i, 4, &blend_map); - } + for i in 0..10 { + screen.set_blended_pixel(0 + i, 0 + i, 1, &blend_map); + screen.set_blended_pixel(319 - i, 0 + i, 2, &blend_map); + screen.set_blended_pixel(0 + i, 239 - i, 3, &blend_map); + screen.set_blended_pixel(319 - i, 239 - i, 4, &blend_map); + } - ////// + ////// - for i in 0..10 { - screen.set_blended_pixel(5-i, 100, 15, &blend_map); - screen.set_blended_pixel(i+314, 100, 15, &blend_map); - screen.set_blended_pixel(160, 5-i, 15, &blend_map); - screen.set_blended_pixel(160, i+234, 15, &blend_map); - } + for i in 0..10 { + screen.set_blended_pixel(5 - i, 100, 15, &blend_map); + screen.set_blended_pixel(i + 314, 100, 15, &blend_map); + screen.set_blended_pixel(160, 5 - i, 15, &blend_map); + screen.set_blended_pixel(160, i + 234, 15, &blend_map); + } - let path = Path::new("tests/ref/blended_pixel_drawing.pcx"); - screen.to_pcx_file(path, &palette).unwrap(); - assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); + let path = Path::new("tests/ref/blended_pixel_drawing.pcx"); + screen.to_pcx_file(path, &palette).unwrap(); + assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); } #[test] fn horiz_line_drawing() { - let (mut screen, palette) = setup(); + let (mut screen, palette) = setup(); - screen.horiz_line(10, 100, 20, 1); - screen.horiz_line(10, 100, 30, 2); + screen.horiz_line(10, 100, 20, 1); + screen.horiz_line(10, 100, 30, 2); - ////// + ////// - screen.horiz_line(-50, 50, 6, 3); - screen.horiz_line(300, 340, 130, 5); + screen.horiz_line(-50, 50, 6, 3); + screen.horiz_line(300, 340, 130, 5); - screen.horiz_line(100, 200, -10, 6); - screen.horiz_line(20, 80, 250, 7); + screen.horiz_line(100, 200, -10, 6); + screen.horiz_line(20, 80, 250, 7); - let path = Path::new("tests/ref/horiz_line_drawing.pcx"); - //screen.to_pcx_file(path, &palette).unwrap(); - assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); + let path = Path::new("tests/ref/horiz_line_drawing.pcx"); + //screen.to_pcx_file(path, &palette).unwrap(); + assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); } #[test] fn blended_horiz_line_drawing() { - let (mut screen, palette, blend_map) = setup_for_blending(); + let (mut screen, palette, blend_map) = setup_for_blending(); - screen.blended_horiz_line(10, 100, 20, 1, &blend_map); - screen.blended_horiz_line(10, 100, 30, 2, &blend_map); + screen.blended_horiz_line(10, 100, 20, 1, &blend_map); + screen.blended_horiz_line(10, 100, 30, 2, &blend_map); - ////// + ////// - screen.blended_horiz_line(-50, 50, 6, 3, &blend_map); - screen.blended_horiz_line(300, 340, 130, 5, &blend_map); + screen.blended_horiz_line(-50, 50, 6, 3, &blend_map); + screen.blended_horiz_line(300, 340, 130, 5, &blend_map); - screen.blended_horiz_line(100, 200, -10, 6, &blend_map); - screen.blended_horiz_line(20, 80, 250, 7, &blend_map); + screen.blended_horiz_line(100, 200, -10, 6, &blend_map); + screen.blended_horiz_line(20, 80, 250, 7, &blend_map); - let path = Path::new("tests/ref/blended_horiz_line_drawing.pcx"); - //screen.to_pcx_file(path, &palette).unwrap(); - assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); + let path = Path::new("tests/ref/blended_horiz_line_drawing.pcx"); + //screen.to_pcx_file(path, &palette).unwrap(); + assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); } #[test] fn vert_line_drawing() { - let (mut screen, palette) = setup(); + let (mut screen, palette) = setup(); - screen.vert_line(50, 10, 200, 1); - screen.vert_line(60, 10, 200, 2); + screen.vert_line(50, 10, 200, 1); + screen.vert_line(60, 10, 200, 2); - ////// + ////// - screen.vert_line(20, -32, 32, 3); - screen.vert_line(270, 245, 165, 5); + screen.vert_line(20, -32, 32, 3); + screen.vert_line(270, 245, 165, 5); - screen.vert_line(-17, 10, 20, 6); - screen.vert_line(400, 100, 300, 7); + screen.vert_line(-17, 10, 20, 6); + screen.vert_line(400, 100, 300, 7); - let path = Path::new("tests/ref/vert_line_drawing.pcx"); - //screen.to_pcx_file(path, &palette).unwrap(); - assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); + let path = Path::new("tests/ref/vert_line_drawing.pcx"); + //screen.to_pcx_file(path, &palette).unwrap(); + assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); } #[test] fn blended_vert_line_drawing() { - let (mut screen, palette, blend_map) = setup_for_blending(); + let (mut screen, palette, blend_map) = setup_for_blending(); - screen.blended_vert_line(50, 10, 200, 1, &blend_map); - screen.blended_vert_line(60, 10, 200, 2, &blend_map); + screen.blended_vert_line(50, 10, 200, 1, &blend_map); + screen.blended_vert_line(60, 10, 200, 2, &blend_map); - ////// + ////// - screen.blended_vert_line(20, -32, 32, 3, &blend_map); - screen.blended_vert_line(270, 245, 165, 5, &blend_map); + screen.blended_vert_line(20, -32, 32, 3, &blend_map); + screen.blended_vert_line(270, 245, 165, 5, &blend_map); - screen.blended_vert_line(-17, 10, 20, 6, &blend_map); - screen.blended_vert_line(400, 100, 300, 7, &blend_map); + screen.blended_vert_line(-17, 10, 20, 6, &blend_map); + screen.blended_vert_line(400, 100, 300, 7, &blend_map); - let path = Path::new("tests/ref/blended_vert_line_drawing.pcx"); - //screen.to_pcx_file(path, &palette).unwrap(); - assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); + let path = Path::new("tests/ref/blended_vert_line_drawing.pcx"); + //screen.to_pcx_file(path, &palette).unwrap(); + assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); } #[test] fn line_drawing() { - let (mut screen, palette) = setup(); + let (mut screen, palette) = setup(); - screen.line(10, 10, 20, 20, 1); - screen.line(10, 100, 20, 150, 2); - screen.line(60, 150, 50, 100, 3); + screen.line(10, 10, 20, 20, 1); + screen.line(10, 100, 20, 150, 2); + screen.line(60, 150, 50, 100, 3); - ////// + ////// - screen.line(50, 10, 100, 10, 5); - screen.line(100, 50, 20, 50, 6); - screen.line(290, 10, 290, 100, 7); - screen.line(310, 100, 310, 10, 8); + screen.line(50, 10, 100, 10, 5); + screen.line(100, 50, 20, 50, 6); + screen.line(290, 10, 290, 100, 7); + screen.line(310, 100, 310, 10, 8); - ////// + ////// - screen.line(50, 200, -50, 200, 5); - screen.line(300, 210, 340, 210, 6); - screen.line(120, -30, 120, 30, 7); - screen.line(130, 200, 130, 270, 8); + screen.line(50, 200, -50, 200, 5); + screen.line(300, 210, 340, 210, 6); + screen.line(120, -30, 120, 30, 7); + screen.line(130, 200, 130, 270, 8); - screen.line(250, 260, 190, 200, 9); - screen.line(180, 30, 240, -30, 10); - screen.line(-20, 140, 20, 180, 11); - screen.line(300, 130, 340, 170, 12); + screen.line(250, 260, 190, 200, 9); + screen.line(180, 30, 240, -30, 10); + screen.line(-20, 140, 20, 180, 11); + screen.line(300, 130, 340, 170, 12); - screen.line(10, -30, 100, -30, 1); - screen.line(70, 250, 170, 250, 2); - screen.line(-100, 120, -100, 239, 3); - screen.line(320, 99, 320, 199, 5); + screen.line(10, -30, 100, -30, 1); + screen.line(70, 250, 170, 250, 2); + screen.line(-100, 120, -100, 239, 3); + screen.line(320, 99, 320, 199, 5); - let path = Path::new("tests/ref/line_drawing.pcx"); - //screen.to_pcx_file(path, &palette).unwrap(); - assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); + let path = Path::new("tests/ref/line_drawing.pcx"); + //screen.to_pcx_file(path, &palette).unwrap(); + assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); } #[test] fn blended_line_drawing() { - let (mut screen, palette, blend_map) = setup_for_blending(); + let (mut screen, palette, blend_map) = setup_for_blending(); - screen.blended_line(10, 10, 20, 20, 1, &blend_map); - screen.blended_line(10, 100, 20, 150, 2, &blend_map); - screen.blended_line(60, 150, 50, 100, 3, &blend_map); + screen.blended_line(10, 10, 20, 20, 1, &blend_map); + screen.blended_line(10, 100, 20, 150, 2, &blend_map); + screen.blended_line(60, 150, 50, 100, 3, &blend_map); - ////// + ////// - screen.blended_line(50, 10, 100, 10, 5, &blend_map); - screen.blended_line(100, 50, 20, 50, 6, &blend_map); - screen.blended_line(290, 10, 290, 100, 7, &blend_map); - screen.blended_line(310, 100, 310, 10, 8, &blend_map); + screen.blended_line(50, 10, 100, 10, 5, &blend_map); + screen.blended_line(100, 50, 20, 50, 6, &blend_map); + screen.blended_line(290, 10, 290, 100, 7, &blend_map); + screen.blended_line(310, 100, 310, 10, 8, &blend_map); - ////// + ////// - screen.blended_line(50, 200, -50, 200, 5, &blend_map); - screen.blended_line(300, 210, 340, 210, 6, &blend_map); - screen.blended_line(120, -30, 120, 30, 7, &blend_map); - screen.blended_line(130, 200, 130, 270, 8, &blend_map); + screen.blended_line(50, 200, -50, 200, 5, &blend_map); + screen.blended_line(300, 210, 340, 210, 6, &blend_map); + screen.blended_line(120, -30, 120, 30, 7, &blend_map); + screen.blended_line(130, 200, 130, 270, 8, &blend_map); - screen.blended_line(250, 260, 190, 200, 9, &blend_map); - screen.blended_line(180, 30, 240, -30, 10, &blend_map); - screen.blended_line(-20, 140, 20, 180, 11, &blend_map); - screen.blended_line(300, 130, 340, 170, 12, &blend_map); + screen.blended_line(250, 260, 190, 200, 9, &blend_map); + screen.blended_line(180, 30, 240, -30, 10, &blend_map); + screen.blended_line(-20, 140, 20, 180, 11, &blend_map); + screen.blended_line(300, 130, 340, 170, 12, &blend_map); - screen.blended_line(10, -30, 100, -30, 1, &blend_map); - screen.blended_line(70, 250, 170, 250, 2, &blend_map); - screen.blended_line(-100, 120, -100, 239, 3, &blend_map); - screen.blended_line(320, 99, 320, 199, 5, &blend_map); + screen.blended_line(10, -30, 100, -30, 1, &blend_map); + screen.blended_line(70, 250, 170, 250, 2, &blend_map); + screen.blended_line(-100, 120, -100, 239, 3, &blend_map); + screen.blended_line(320, 99, 320, 199, 5, &blend_map); - let path = Path::new("tests/ref/blended_line_drawing.pcx"); - //screen.to_pcx_file(path, &palette).unwrap(); - assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); + let path = Path::new("tests/ref/blended_line_drawing.pcx"); + //screen.to_pcx_file(path, &palette).unwrap(); + assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); } #[test] fn rect_drawing() { - let (mut screen, palette) = setup(); + let (mut screen, palette) = setup(); - screen.rect(10, 10, 90, 90, 1); - screen.rect(10, 110, 90, 190, 2); - screen.rect(190, 90, 110, 10, 3); + screen.rect(10, 10, 90, 90, 1); + screen.rect(10, 110, 90, 190, 2); + screen.rect(190, 90, 110, 10, 3); - ////// + ////// - screen.rect(-8, 10, 7, 25, 5); - screen.rect(20, -8, 35, 7, 6); - screen.rect(313, 170, 328, 185, 7); - screen.rect(285, 233, 300, 248, 8); + screen.rect(-8, 10, 7, 25, 5); + screen.rect(20, -8, 35, 7, 6); + screen.rect(313, 170, 328, 185, 7); + screen.rect(285, 233, 300, 248, 8); - screen.rect(-16, 30, -1, 46, 9); - screen.rect(40, -16, 55, -1, 10); - screen.rect(320, 150, 335, 165, 11); - screen.rect(265, 240, 280, 255, 12); + screen.rect(-16, 30, -1, 46, 9); + screen.rect(40, -16, 55, -1, 10); + screen.rect(320, 150, 335, 165, 11); + screen.rect(265, 240, 280, 255, 12); - screen.rect(300, 20, 340, -20, 13); - screen.rect(20, 220, -20, 260, 14); + screen.rect(300, 20, 340, -20, 13); + screen.rect(20, 220, -20, 260, 14); - let path = Path::new("tests/ref/rect_drawing.pcx"); - //screen.to_pcx_file(path, &palette).unwrap(); - assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); + let path = Path::new("tests/ref/rect_drawing.pcx"); + //screen.to_pcx_file(path, &palette).unwrap(); + assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); } #[test] fn blended_rect_drawing() { - let (mut screen, palette, blend_map) = setup_for_blending(); + let (mut screen, palette, blend_map) = setup_for_blending(); - screen.blended_rect(10, 10, 90, 90, 1, &blend_map); - screen.blended_rect(10, 110, 90, 190, 2, &blend_map); - screen.blended_rect(190, 90, 110, 10, 3, &blend_map); + screen.blended_rect(10, 10, 90, 90, 1, &blend_map); + screen.blended_rect(10, 110, 90, 190, 2, &blend_map); + screen.blended_rect(190, 90, 110, 10, 3, &blend_map); - ////// + ////// - screen.blended_rect(-8, 10, 7, 25, 5, &blend_map); - screen.blended_rect(20, -8, 35, 7, 6, &blend_map); - screen.blended_rect(313, 170, 328, 185, 7, &blend_map); - screen.blended_rect(285, 233, 300, 248, 8, &blend_map); + screen.blended_rect(-8, 10, 7, 25, 5, &blend_map); + screen.blended_rect(20, -8, 35, 7, 6, &blend_map); + screen.blended_rect(313, 170, 328, 185, 7, &blend_map); + screen.blended_rect(285, 233, 300, 248, 8, &blend_map); - screen.blended_rect(-16, 30, -1, 46, 9, &blend_map); - screen.blended_rect(40, -16, 55, -1, 10, &blend_map); - screen.blended_rect(320, 150, 335, 165, 11, &blend_map); - screen.blended_rect(265, 240, 280, 255, 12, &blend_map); + screen.blended_rect(-16, 30, -1, 46, 9, &blend_map); + screen.blended_rect(40, -16, 55, -1, 10, &blend_map); + screen.blended_rect(320, 150, 335, 165, 11, &blend_map); + screen.blended_rect(265, 240, 280, 255, 12, &blend_map); - screen.blended_rect(300, 20, 340, -20, 13, &blend_map); - screen.blended_rect(20, 220, -20, 260, 14, &blend_map); + screen.blended_rect(300, 20, 340, -20, 13, &blend_map); + screen.blended_rect(20, 220, -20, 260, 14, &blend_map); - let path = Path::new("tests/ref/blended_rect_drawing.pcx"); - //screen.to_pcx_file(path, &palette).unwrap(); - assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); + let path = Path::new("tests/ref/blended_rect_drawing.pcx"); + //screen.to_pcx_file(path, &palette).unwrap(); + assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); } #[test] fn filled_rect_drawing() { - let (mut screen, palette) = setup(); + let (mut screen, palette) = setup(); - screen.filled_rect(10, 10, 90, 90, 1); - screen.filled_rect(10, 110, 90, 190, 2); - screen.filled_rect(190, 90, 110, 10, 3); + screen.filled_rect(10, 10, 90, 90, 1); + screen.filled_rect(10, 110, 90, 190, 2); + screen.filled_rect(190, 90, 110, 10, 3); - ////// + ////// - screen.filled_rect(-8, 10, 7, 25, 5); - screen.filled_rect(20, -8, 35, 7, 6); - screen.filled_rect(313, 170, 328, 185, 7); - screen.filled_rect(285, 233, 300, 248, 8); + screen.filled_rect(-8, 10, 7, 25, 5); + screen.filled_rect(20, -8, 35, 7, 6); + screen.filled_rect(313, 170, 328, 185, 7); + screen.filled_rect(285, 233, 300, 248, 8); - screen.filled_rect(-16, 30, -1, 46, 9); - screen.filled_rect(40, -16, 55, -1, 10); - screen.filled_rect(320, 150, 335, 165, 11); - screen.filled_rect(265, 240, 280, 255, 12); + screen.filled_rect(-16, 30, -1, 46, 9); + screen.filled_rect(40, -16, 55, -1, 10); + screen.filled_rect(320, 150, 335, 165, 11); + screen.filled_rect(265, 240, 280, 255, 12); - screen.filled_rect(300, 20, 340, -20, 13); - screen.filled_rect(20, 220, -20, 260, 14); + screen.filled_rect(300, 20, 340, -20, 13); + screen.filled_rect(20, 220, -20, 260, 14); - let path = Path::new("tests/ref/filled_rect_drawing.pcx"); - //screen.to_pcx_file(path, &palette).unwrap(); - assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); + let path = Path::new("tests/ref/filled_rect_drawing.pcx"); + //screen.to_pcx_file(path, &palette).unwrap(); + assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); } #[test] fn blended_filled_rect_drawing() { - let (mut screen, palette, blend_map) = setup_for_blending(); + let (mut screen, palette, blend_map) = setup_for_blending(); - screen.blended_filled_rect(10, 10, 90, 90, 1, &blend_map); - screen.blended_filled_rect(10, 110, 90, 190, 2, &blend_map); - screen.blended_filled_rect(190, 90, 110, 10, 3, &blend_map); + screen.blended_filled_rect(10, 10, 90, 90, 1, &blend_map); + screen.blended_filled_rect(10, 110, 90, 190, 2, &blend_map); + screen.blended_filled_rect(190, 90, 110, 10, 3, &blend_map); - ////// + ////// - screen.blended_filled_rect(-8, 10, 7, 25, 5, &blend_map); - screen.blended_filled_rect(20, -8, 35, 7, 6, &blend_map); - screen.blended_filled_rect(313, 170, 328, 185, 7, &blend_map); - screen.blended_filled_rect(285, 233, 300, 248, 8, &blend_map); + screen.blended_filled_rect(-8, 10, 7, 25, 5, &blend_map); + screen.blended_filled_rect(20, -8, 35, 7, 6, &blend_map); + screen.blended_filled_rect(313, 170, 328, 185, 7, &blend_map); + screen.blended_filled_rect(285, 233, 300, 248, 8, &blend_map); - screen.blended_filled_rect(-16, 30, -1, 46, 9, &blend_map); - screen.blended_filled_rect(40, -16, 55, -1, 10, &blend_map); - screen.blended_filled_rect(320, 150, 335, 165, 11, &blend_map); - screen.blended_filled_rect(265, 240, 280, 255, 12, &blend_map); + screen.blended_filled_rect(-16, 30, -1, 46, 9, &blend_map); + screen.blended_filled_rect(40, -16, 55, -1, 10, &blend_map); + screen.blended_filled_rect(320, 150, 335, 165, 11, &blend_map); + screen.blended_filled_rect(265, 240, 280, 255, 12, &blend_map); - screen.blended_filled_rect(300, 20, 340, -20, 13, &blend_map); - screen.blended_filled_rect(20, 220, -20, 260, 14, &blend_map); + screen.blended_filled_rect(300, 20, 340, -20, 13, &blend_map); + screen.blended_filled_rect(20, 220, -20, 260, 14, &blend_map); - let path = Path::new("tests/ref/blended_filled_rect_drawing.pcx"); - //screen.to_pcx_file(path, &palette).unwrap(); - assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); + let path = Path::new("tests/ref/blended_filled_rect_drawing.pcx"); + //screen.to_pcx_file(path, &palette).unwrap(); + assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); } #[test] fn circle_drawing() { - let (mut screen, palette) = setup(); + let (mut screen, palette) = setup(); - screen.circle(48, 48, 32, 1); - screen.circle(128, 48, 24, 2); - screen.circle(48, 128, 40, 3); + screen.circle(48, 48, 32, 1); + screen.circle(128, 48, 24, 2); + screen.circle(48, 128, 40, 3); - ////// + ////// - screen.circle(0, 30, 16, 5); - screen.circle(40, 2, 11, 6); - screen.circle(319, 211, 17, 7); - screen.circle(290, 241, 21, 8); + screen.circle(0, 30, 16, 5); + screen.circle(40, 2, 11, 6); + screen.circle(319, 211, 17, 7); + screen.circle(290, 241, 21, 8); - screen.circle(319, 1, 22, 9); - screen.circle(2, 242, 19, 10); + screen.circle(319, 1, 22, 9); + screen.circle(2, 242, 19, 10); - let path = Path::new("tests/ref/circle_drawing.pcx"); - //screen.to_pcx_file(path, &palette).unwrap(); - assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); + let path = Path::new("tests/ref/circle_drawing.pcx"); + //screen.to_pcx_file(path, &palette).unwrap(); + assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); } #[test] fn filled_circle_drawing() { - let (mut screen, palette) = setup(); + let (mut screen, palette) = setup(); - screen.filled_circle(48, 48, 32, 1); - screen.filled_circle(128, 48, 24, 2); - screen.filled_circle(48, 128, 40, 3); + screen.filled_circle(48, 48, 32, 1); + screen.filled_circle(128, 48, 24, 2); + screen.filled_circle(48, 128, 40, 3); - ////// + ////// - screen.filled_circle(0, 30, 16, 5); - screen.filled_circle(40, 2, 11, 6); - screen.filled_circle(319, 211, 17, 7); - screen.filled_circle(290, 241, 21, 8); + screen.filled_circle(0, 30, 16, 5); + screen.filled_circle(40, 2, 11, 6); + screen.filled_circle(319, 211, 17, 7); + screen.filled_circle(290, 241, 21, 8); - screen.filled_circle(319, 1, 22, 9); - screen.filled_circle(2, 242, 19, 10); + screen.filled_circle(319, 1, 22, 9); + screen.filled_circle(2, 242, 19, 10); - let path = Path::new("tests/ref/filled_circle_drawing.pcx"); - //screen.to_pcx_file(path, &palette).unwrap(); - assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); + let path = Path::new("tests/ref/filled_circle_drawing.pcx"); + //screen.to_pcx_file(path, &palette).unwrap(); + assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); } #[test] fn text_drawing() { - let (mut screen, palette) = setup(); + let (mut screen, palette) = setup(); - let font = BitmaskFont::new_vga_font().unwrap(); - let small_font = BitmaskFont::load_from_file(Path::new("./test-assets/small.fnt")).unwrap(); - let chunky_font = BitmaskFont::load_from_file(Path::new("./test-assets/chunky.fnt")).unwrap(); + let font = BitmaskFont::new_vga_font().unwrap(); + let small_font = BitmaskFont::load_from_file(Path::new("./test-assets/small.fnt")).unwrap(); + let chunky_font = BitmaskFont::load_from_file(Path::new("./test-assets/chunky.fnt")).unwrap(); - let message = "Hello, world! HELLO, WORLD!\nTesting 123"; + let message = "Hello, world! HELLO, WORLD!\nTesting 123"; - screen.print_string(message, 20, 20, FontRenderOpts::Color(1), &font); - screen.print_string(message, 20, 40, FontRenderOpts::Color(2), &small_font); - screen.print_string(message, 20, 60, FontRenderOpts::Color(3), &chunky_font); + screen.print_string(message, 20, 20, FontRenderOpts::Color(1), &font); + screen.print_string(message, 20, 40, FontRenderOpts::Color(2), &small_font); + screen.print_string(message, 20, 60, FontRenderOpts::Color(3), &chunky_font); - screen.filled_rect(58, 218, 162, 230, 7); - screen.print_string("transparency!", 60, 220, FontRenderOpts::Color(9), &font); + screen.filled_rect(58, 218, 162, 230, 7); + screen.print_string("transparency!", 60, 220, FontRenderOpts::Color(9), &font); - let mut s = String::with_capacity(256); - for i in 1..=127 { - if i % 8 == 0 { - s += "\n"; - } - if i == 10 { - s += " "; - } else { - s += &char::from(i).to_string(); - } - } + let mut s = String::with_capacity(256); + for i in 1..=127 { + if i % 8 == 0 { + s += "\n"; + } + if i == 10 { + s += " "; + } else { + s += &char::from(i).to_string(); + } + } - screen.print_string(&s, 20, 80, FontRenderOpts::Color(15), &font); - screen.print_string(&s, 110, 80, FontRenderOpts::Color(15), &small_font); - screen.print_string(&s, 190, 80, FontRenderOpts::Color(15), &chunky_font); + screen.print_string(&s, 20, 80, FontRenderOpts::Color(15), &font); + screen.print_string(&s, 110, 80, FontRenderOpts::Color(15), &small_font); + screen.print_string(&s, 190, 80, FontRenderOpts::Color(15), &chunky_font); - ////// + ////// - let message = "Hello, world!"; + let message = "Hello, world!"; - screen.print_string(message, -35, 10, FontRenderOpts::Color(9), &font); - screen.print_string(message, 80, -4, FontRenderOpts::Color(10), &font); - screen.print_string(message, 285, 120, FontRenderOpts::Color(11), &font); - screen.print_string(message, 200, 236, FontRenderOpts::Color(12), &font); - screen.print_string(message, -232, 10, FontRenderOpts::Color(5), &font); - screen.print_string(message, 80, -24, FontRenderOpts::Color(6), &font); - screen.print_string(message, 360, 120, FontRenderOpts::Color(7), &font); - screen.print_string(message, 200, 250, FontRenderOpts::Color(8), &font); + screen.print_string(message, -35, 10, FontRenderOpts::Color(9), &font); + screen.print_string(message, 80, -4, FontRenderOpts::Color(10), &font); + screen.print_string(message, 285, 120, FontRenderOpts::Color(11), &font); + screen.print_string(message, 200, 236, FontRenderOpts::Color(12), &font); + screen.print_string(message, -232, 10, FontRenderOpts::Color(5), &font); + screen.print_string(message, 80, -24, FontRenderOpts::Color(6), &font); + screen.print_string(message, 360, 120, FontRenderOpts::Color(7), &font); + screen.print_string(message, 200, 250, FontRenderOpts::Color(8), &font); - let path = Path::new("tests/ref/text_drawing.pcx"); - //screen.to_pcx_file(path, &palette).unwrap(); - assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); + let path = Path::new("tests/ref/text_drawing.pcx"); + //screen.to_pcx_file(path, &palette).unwrap(); + assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); } fn generate_bitmap(width: i32, height: i32) -> Bitmap { - let x_third = width / 3; - let y_third = height / 3; + let x_third = width / 3; + let y_third = height / 3; - let mut bitmap = Bitmap::new(width as u32, height as u32).unwrap(); + let mut bitmap = Bitmap::new(width as u32, height as u32).unwrap(); - bitmap.filled_rect(0, 0, x_third, y_third, 1); - bitmap.filled_rect(x_third*2+1, y_third*2+1, width-1, height-1, 2); - bitmap.filled_rect(0, y_third*2+1, x_third, height-1, 3); - bitmap.filled_rect(x_third*2+1, 0, width-1, y_third, 4); - bitmap.filled_rect(x_third, y_third, x_third*2+1, y_third*2+1, 5); - bitmap.rect(0, 0, width-1, height-1, 6); + bitmap.filled_rect(0, 0, x_third, y_third, 1); + bitmap.filled_rect(x_third * 2 + 1, y_third * 2 + 1, width - 1, height - 1, 2); + bitmap.filled_rect(0, y_third * 2 + 1, x_third, height - 1, 3); + bitmap.filled_rect(x_third * 2 + 1, 0, width - 1, y_third, 4); + bitmap.filled_rect(x_third, y_third, x_third * 2 + 1, y_third * 2 + 1, 5); + bitmap.rect(0, 0, width - 1, height - 1, 6); - bitmap + bitmap } #[test] fn solid_blits() { - let (mut screen, palette) = setup(); - screen.clear(247); + let (mut screen, palette) = setup(); + screen.clear(247); - let bmp16 = generate_bitmap(16, 16); - let bmp12 = generate_bitmap(12, 12); - let bmp21 = generate_bitmap(21, 21); - let bmp3 = generate_bitmap(3, 3); + let bmp16 = generate_bitmap(16, 16); + let bmp12 = generate_bitmap(12, 12); + let bmp21 = generate_bitmap(21, 21); + let bmp3 = generate_bitmap(3, 3); - let x = 40; - let y = 20; - screen.blit(BlitMethod::Solid, &bmp16, x+16, y+48); - screen.blit(BlitMethod::Solid, &bmp12, x+80, y+48); - screen.blit(BlitMethod::Solid, &bmp21, x+144, y+48); - screen.blit(BlitMethod::Solid, &bmp3, x+208, y+48); + let x = 40; + let y = 20; + screen.blit(BlitMethod::Solid, &bmp16, x + 16, y + 48); + screen.blit(BlitMethod::Solid, &bmp12, x + 80, y + 48); + screen.blit(BlitMethod::Solid, &bmp21, x + 144, y + 48); + screen.blit(BlitMethod::Solid, &bmp3, x + 208, y + 48); - let x = 40; - let y = 110; - unsafe { - screen.blit_unchecked(BlitMethod::Solid, &bmp16, x+16, y+48); - screen.blit_unchecked(BlitMethod::Solid, &bmp12, x+80, y+48); - screen.blit_unchecked(BlitMethod::Solid, &bmp21, x+144, y+48); - screen.blit_unchecked(BlitMethod::Solid, &bmp3, x+208, y+48); - } + let x = 40; + let y = 110; + unsafe { + screen.blit_unchecked(BlitMethod::Solid, &bmp16, x + 16, y + 48); + screen.blit_unchecked(BlitMethod::Solid, &bmp12, x + 80, y + 48); + screen.blit_unchecked(BlitMethod::Solid, &bmp21, x + 144, y + 48); + screen.blit_unchecked(BlitMethod::Solid, &bmp3, x + 208, y + 48); + } - ////// + ////// - screen.blit(BlitMethod::Solid, &bmp16, -3, 46); - screen.blit(BlitMethod::Solid, &bmp16, -4, 76); - screen.blit(BlitMethod::Solid, &bmp16, -8, 106); - screen.blit(BlitMethod::Solid, &bmp16, -12, 136); - screen.blit(BlitMethod::Solid, &bmp16, -13, 166); - screen.blit(BlitMethod::Solid, &bmp16, -14, 196); - screen.blit(BlitMethod::Solid, &bmp16, -16, 226); + screen.blit(BlitMethod::Solid, &bmp16, -3, 46); + screen.blit(BlitMethod::Solid, &bmp16, -4, 76); + screen.blit(BlitMethod::Solid, &bmp16, -8, 106); + screen.blit(BlitMethod::Solid, &bmp16, -12, 136); + screen.blit(BlitMethod::Solid, &bmp16, -13, 166); + screen.blit(BlitMethod::Solid, &bmp16, -14, 196); + screen.blit(BlitMethod::Solid, &bmp16, -16, 226); - screen.blit(BlitMethod::Solid, &bmp16, 46, -3); - screen.blit(BlitMethod::Solid, &bmp16, 76, -4); - screen.blit(BlitMethod::Solid, &bmp16, 106, -8); - screen.blit(BlitMethod::Solid, &bmp16, 136, -12); - screen.blit(BlitMethod::Solid, &bmp16, 166, -13); - screen.blit(BlitMethod::Solid, &bmp16, 196, -14); - screen.blit(BlitMethod::Solid, &bmp16, 226, -16); + screen.blit(BlitMethod::Solid, &bmp16, 46, -3); + screen.blit(BlitMethod::Solid, &bmp16, 76, -4); + screen.blit(BlitMethod::Solid, &bmp16, 106, -8); + screen.blit(BlitMethod::Solid, &bmp16, 136, -12); + screen.blit(BlitMethod::Solid, &bmp16, 166, -13); + screen.blit(BlitMethod::Solid, &bmp16, 196, -14); + screen.blit(BlitMethod::Solid, &bmp16, 226, -16); - screen.blit(BlitMethod::Solid, &bmp16, 307, 46); - screen.blit(BlitMethod::Solid, &bmp16, 308, 76); - screen.blit(BlitMethod::Solid, &bmp16, 312, 106); - screen.blit(BlitMethod::Solid, &bmp16, 316, 136); - screen.blit(BlitMethod::Solid, &bmp16, 317, 166); - screen.blit(BlitMethod::Solid, &bmp16, 318, 196); - screen.blit(BlitMethod::Solid, &bmp16, 320, 226); + screen.blit(BlitMethod::Solid, &bmp16, 307, 46); + screen.blit(BlitMethod::Solid, &bmp16, 308, 76); + screen.blit(BlitMethod::Solid, &bmp16, 312, 106); + screen.blit(BlitMethod::Solid, &bmp16, 316, 136); + screen.blit(BlitMethod::Solid, &bmp16, 317, 166); + screen.blit(BlitMethod::Solid, &bmp16, 318, 196); + screen.blit(BlitMethod::Solid, &bmp16, 320, 226); - screen.blit(BlitMethod::Solid, &bmp16, 46, 227); - screen.blit(BlitMethod::Solid, &bmp16, 76, 228); - screen.blit(BlitMethod::Solid, &bmp16, 106, 232); - screen.blit(BlitMethod::Solid, &bmp16, 136, 236); - screen.blit(BlitMethod::Solid, &bmp16, 166, 237); - screen.blit(BlitMethod::Solid, &bmp16, 196, 238); - screen.blit(BlitMethod::Solid, &bmp16, 226, 240); + screen.blit(BlitMethod::Solid, &bmp16, 46, 227); + screen.blit(BlitMethod::Solid, &bmp16, 76, 228); + screen.blit(BlitMethod::Solid, &bmp16, 106, 232); + screen.blit(BlitMethod::Solid, &bmp16, 136, 236); + screen.blit(BlitMethod::Solid, &bmp16, 166, 237); + screen.blit(BlitMethod::Solid, &bmp16, 196, 238); + screen.blit(BlitMethod::Solid, &bmp16, 226, 240); - let path = Path::new("tests/ref/solid_blits.pcx"); - //screen.to_pcx_file(path, &palette).unwrap(); - assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); + let path = Path::new("tests/ref/solid_blits.pcx"); + //screen.to_pcx_file(path, &palette).unwrap(); + assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); } #[test] fn blended_solid_blits() { - let (mut screen, palette, blend_map) = setup_for_blending(); - let blend_map = Rc::new(blend_map); + let (mut screen, palette, blend_map) = setup_for_blending(); + let blend_map = Rc::new(blend_map); - let bmp16 = generate_bitmap(16, 16); - let bmp12 = generate_bitmap(12, 12); - let bmp21 = generate_bitmap(21, 21); - let bmp3 = generate_bitmap(3, 3); + let bmp16 = generate_bitmap(16, 16); + let bmp12 = generate_bitmap(12, 12); + let bmp21 = generate_bitmap(21, 21); + let bmp3 = generate_bitmap(3, 3); - let x = 40; - let y = 20; - screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, x+16, y+48); - screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp12, x+80, y+48); - screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp21, x+144, y+48); - screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp3, x+208, y+48); + let x = 40; + let y = 20; + screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, x + 16, y + 48); + screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp12, x + 80, y + 48); + screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp21, x + 144, y + 48); + screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp3, x + 208, y + 48); - let x = 40; - let y = 110; - unsafe { - screen.blit_unchecked(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, x+16, y+48); - screen.blit_unchecked(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp12, x+80, y+48); - screen.blit_unchecked(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp21, x+144, y+48); - screen.blit_unchecked(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp3, x+208, y+48); - } + let x = 40; + let y = 110; + unsafe { + screen.blit_unchecked(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, x + 16, y + 48); + screen.blit_unchecked(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp12, x + 80, y + 48); + screen.blit_unchecked(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp21, x + 144, y + 48); + screen.blit_unchecked(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp3, x + 208, y + 48); + } - ////// + ////// - screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, -3, 46); - screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, -4, 76); - screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, -8, 106); - screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, -12, 136); - screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, -13, 166); - screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, -14, 196); - screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, -16, 226); + screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, -3, 46); + screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, -4, 76); + screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, -8, 106); + screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, -12, 136); + screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, -13, 166); + screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, -14, 196); + screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, -16, 226); - screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, 46, -3); - screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, 76, -4); - screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, 106, -8); - screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, 136, -12); - screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, 166, -13); - screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, 196, -14); - screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, 226, -16); + screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, 46, -3); + screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, 76, -4); + screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, 106, -8); + screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, 136, -12); + screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, 166, -13); + screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, 196, -14); + screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, 226, -16); - screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, 307, 46); - screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, 308, 76); - screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, 312, 106); - screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, 316, 136); - screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, 317, 166); - screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, 318, 196); - screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, 320, 226); + screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, 307, 46); + screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, 308, 76); + screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, 312, 106); + screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, 316, 136); + screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, 317, 166); + screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, 318, 196); + screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, 320, 226); - screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, 46, 227); - screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, 76, 228); - screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, 106, 232); - screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, 136, 236); - screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, 166, 237); - screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, 196, 238); - screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, 226, 240); + screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, 46, 227); + screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, 76, 228); + screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, 106, 232); + screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, 136, 236); + screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, 166, 237); + screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, 196, 238); + screen.blit(BlitMethod::SolidBlended { blend_map: blend_map.clone() }, &bmp16, 226, 240); - let path = Path::new("tests/ref/blended_solid_blits.pcx"); - //screen.to_pcx_file(path, &palette).unwrap(); - assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); + let path = Path::new("tests/ref/blended_solid_blits.pcx"); + //screen.to_pcx_file(path, &palette).unwrap(); + assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); } #[test] fn solid_flipped_blits() { - let (mut screen, palette) = setup(); - screen.clear(247); + let (mut screen, palette) = setup(); + screen.clear(247); - let bmp = generate_bitmap(16, 16); + let bmp = generate_bitmap(16, 16); - let x = 40; - let y = 20; - screen.blit(BlitMethod::SolidFlipped { horizontal_flip: false, vertical_flip: false }, &bmp, x+16, y+48); - screen.blit(BlitMethod::SolidFlipped { horizontal_flip: true, vertical_flip: false }, &bmp, x+80, y+48); - screen.blit(BlitMethod::SolidFlipped { horizontal_flip: false, vertical_flip: true }, &bmp, x+144, y+48); - screen.blit(BlitMethod::SolidFlipped { horizontal_flip: true, vertical_flip: true }, &bmp, x+208, y+48); + let x = 40; + let y = 20; + screen.blit(BlitMethod::SolidFlipped { horizontal_flip: false, vertical_flip: false }, &bmp, x + 16, y + 48); + screen.blit(BlitMethod::SolidFlipped { horizontal_flip: true, vertical_flip: false }, &bmp, x + 80, y + 48); + screen.blit(BlitMethod::SolidFlipped { horizontal_flip: false, vertical_flip: true }, &bmp, x + 144, y + 48); + screen.blit(BlitMethod::SolidFlipped { horizontal_flip: true, vertical_flip: true }, &bmp, x + 208, y + 48); - let x = 40; - let y = 110; - unsafe { - screen.blit_unchecked(BlitMethod::SolidFlipped { horizontal_flip: false, vertical_flip: false }, &bmp, x+16, y+48); - screen.blit_unchecked(BlitMethod::SolidFlipped { horizontal_flip: true, vertical_flip: false }, &bmp, x+80, y+48); - screen.blit_unchecked(BlitMethod::SolidFlipped { horizontal_flip: false, vertical_flip: true }, &bmp, x+144, y+48); - screen.blit_unchecked(BlitMethod::SolidFlipped { horizontal_flip: true, vertical_flip: true }, &bmp, x+208, y+48); - } + let x = 40; + let y = 110; + unsafe { + screen.blit_unchecked(BlitMethod::SolidFlipped { horizontal_flip: false, vertical_flip: false }, &bmp, x + 16, y + 48); + screen.blit_unchecked(BlitMethod::SolidFlipped { horizontal_flip: true, vertical_flip: false }, &bmp, x + 80, y + 48); + screen.blit_unchecked(BlitMethod::SolidFlipped { horizontal_flip: false, vertical_flip: true }, &bmp, x + 144, y + 48); + screen.blit_unchecked(BlitMethod::SolidFlipped { horizontal_flip: true, vertical_flip: true }, &bmp, x + 208, y + 48); + } - ////// + ////// - screen.blit(BlitMethod::SolidFlipped { horizontal_flip: false, vertical_flip: false }, &bmp, -3, 46); - screen.blit(BlitMethod::SolidFlipped { horizontal_flip: true, vertical_flip: false }, &bmp, -4, 76); - screen.blit(BlitMethod::SolidFlipped { horizontal_flip: false, vertical_flip: true }, &bmp, -8, 106); - screen.blit(BlitMethod::SolidFlipped { horizontal_flip: true, vertical_flip: true }, &bmp, -12, 136); - screen.blit(BlitMethod::SolidFlipped { horizontal_flip: false, vertical_flip: true }, &bmp, -13, 166); - screen.blit(BlitMethod::SolidFlipped { horizontal_flip: true, vertical_flip: false }, &bmp, -14, 196); - screen.blit(BlitMethod::SolidFlipped { horizontal_flip: false, vertical_flip: true }, &bmp, -16, 226); + screen.blit(BlitMethod::SolidFlipped { horizontal_flip: false, vertical_flip: false }, &bmp, -3, 46); + screen.blit(BlitMethod::SolidFlipped { horizontal_flip: true, vertical_flip: false }, &bmp, -4, 76); + screen.blit(BlitMethod::SolidFlipped { horizontal_flip: false, vertical_flip: true }, &bmp, -8, 106); + screen.blit(BlitMethod::SolidFlipped { horizontal_flip: true, vertical_flip: true }, &bmp, -12, 136); + screen.blit(BlitMethod::SolidFlipped { horizontal_flip: false, vertical_flip: true }, &bmp, -13, 166); + screen.blit(BlitMethod::SolidFlipped { horizontal_flip: true, vertical_flip: false }, &bmp, -14, 196); + screen.blit(BlitMethod::SolidFlipped { horizontal_flip: false, vertical_flip: true }, &bmp, -16, 226); - screen.blit(BlitMethod::SolidFlipped { horizontal_flip: false, vertical_flip: false }, &bmp, 46, -3); - screen.blit(BlitMethod::SolidFlipped { horizontal_flip: true, vertical_flip: false }, &bmp, 76, -4); - screen.blit(BlitMethod::SolidFlipped { horizontal_flip: false, vertical_flip: true }, &bmp, 106, -8); - screen.blit(BlitMethod::SolidFlipped { horizontal_flip: true, vertical_flip: true }, &bmp, 136, -12); - screen.blit(BlitMethod::SolidFlipped { horizontal_flip: false, vertical_flip: false }, &bmp, 166, -13); - screen.blit(BlitMethod::SolidFlipped { horizontal_flip: true, vertical_flip: false }, &bmp, 196, -14); - screen.blit(BlitMethod::SolidFlipped { horizontal_flip: false, vertical_flip: true }, &bmp, 226, -16); + screen.blit(BlitMethod::SolidFlipped { horizontal_flip: false, vertical_flip: false }, &bmp, 46, -3); + screen.blit(BlitMethod::SolidFlipped { horizontal_flip: true, vertical_flip: false }, &bmp, 76, -4); + screen.blit(BlitMethod::SolidFlipped { horizontal_flip: false, vertical_flip: true }, &bmp, 106, -8); + screen.blit(BlitMethod::SolidFlipped { horizontal_flip: true, vertical_flip: true }, &bmp, 136, -12); + screen.blit(BlitMethod::SolidFlipped { horizontal_flip: false, vertical_flip: false }, &bmp, 166, -13); + screen.blit(BlitMethod::SolidFlipped { horizontal_flip: true, vertical_flip: false }, &bmp, 196, -14); + screen.blit(BlitMethod::SolidFlipped { horizontal_flip: false, vertical_flip: true }, &bmp, 226, -16); - screen.blit(BlitMethod::SolidFlipped { horizontal_flip: false, vertical_flip: false }, &bmp, 307, 46); - screen.blit(BlitMethod::SolidFlipped { horizontal_flip: true, vertical_flip: false }, &bmp, 308, 76); - screen.blit(BlitMethod::SolidFlipped { horizontal_flip: false, vertical_flip: true }, &bmp, 312, 106); - screen.blit(BlitMethod::SolidFlipped { horizontal_flip: true, vertical_flip: true }, &bmp, 316, 136); - screen.blit(BlitMethod::SolidFlipped { horizontal_flip: false, vertical_flip: false }, &bmp, 317, 166); - screen.blit(BlitMethod::SolidFlipped { horizontal_flip: true, vertical_flip: false }, &bmp, 318, 196); - screen.blit(BlitMethod::SolidFlipped { horizontal_flip: false, vertical_flip: true }, &bmp, 320, 226); + screen.blit(BlitMethod::SolidFlipped { horizontal_flip: false, vertical_flip: false }, &bmp, 307, 46); + screen.blit(BlitMethod::SolidFlipped { horizontal_flip: true, vertical_flip: false }, &bmp, 308, 76); + screen.blit(BlitMethod::SolidFlipped { horizontal_flip: false, vertical_flip: true }, &bmp, 312, 106); + screen.blit(BlitMethod::SolidFlipped { horizontal_flip: true, vertical_flip: true }, &bmp, 316, 136); + screen.blit(BlitMethod::SolidFlipped { horizontal_flip: false, vertical_flip: false }, &bmp, 317, 166); + screen.blit(BlitMethod::SolidFlipped { horizontal_flip: true, vertical_flip: false }, &bmp, 318, 196); + screen.blit(BlitMethod::SolidFlipped { horizontal_flip: false, vertical_flip: true }, &bmp, 320, 226); - screen.blit(BlitMethod::SolidFlipped { horizontal_flip: false, vertical_flip: false }, &bmp, 46, 227); - screen.blit(BlitMethod::SolidFlipped { horizontal_flip: true, vertical_flip: false }, &bmp, 76, 228); - screen.blit(BlitMethod::SolidFlipped { horizontal_flip: false, vertical_flip: true }, &bmp, 106, 232); - screen.blit(BlitMethod::SolidFlipped { horizontal_flip: true, vertical_flip: true }, &bmp, 136, 236); - screen.blit(BlitMethod::SolidFlipped { horizontal_flip: false, vertical_flip: false }, &bmp, 166, 237); - screen.blit(BlitMethod::SolidFlipped { horizontal_flip: true, vertical_flip: false }, &bmp, 196, 238); - screen.blit(BlitMethod::SolidFlipped { horizontal_flip: false, vertical_flip: true }, &bmp, 226, 240); + screen.blit(BlitMethod::SolidFlipped { horizontal_flip: false, vertical_flip: false }, &bmp, 46, 227); + screen.blit(BlitMethod::SolidFlipped { horizontal_flip: true, vertical_flip: false }, &bmp, 76, 228); + screen.blit(BlitMethod::SolidFlipped { horizontal_flip: false, vertical_flip: true }, &bmp, 106, 232); + screen.blit(BlitMethod::SolidFlipped { horizontal_flip: true, vertical_flip: true }, &bmp, 136, 236); + screen.blit(BlitMethod::SolidFlipped { horizontal_flip: false, vertical_flip: false }, &bmp, 166, 237); + screen.blit(BlitMethod::SolidFlipped { horizontal_flip: true, vertical_flip: false }, &bmp, 196, 238); + screen.blit(BlitMethod::SolidFlipped { horizontal_flip: false, vertical_flip: true }, &bmp, 226, 240); - let path = Path::new("tests/ref/solid_flipped_blits.pcx"); - //screen.to_pcx_file(path, &palette).unwrap(); - assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); + let path = Path::new("tests/ref/solid_flipped_blits.pcx"); + //screen.to_pcx_file(path, &palette).unwrap(); + assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); } #[test] fn blended_solid_flipped_blits() { - let (mut screen, palette, blend_map) = setup_for_blending(); - let blend_map = Rc::new(blend_map); + let (mut screen, palette, blend_map) = setup_for_blending(); + let blend_map = Rc::new(blend_map); - let bmp = generate_bitmap(16, 16); + let bmp = generate_bitmap(16, 16); - let x = 40; - let y = 20; - screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: false, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, x+16, y+48); - screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: true, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, x+80, y+48); - screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: false, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, x+144, y+48); - screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: true, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, x+208, y+48); + let x = 40; + let y = 20; + screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: false, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, x + 16, y + 48); + screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: true, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, x + 80, y + 48); + screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: false, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, x + 144, y + 48); + screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: true, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, x + 208, y + 48); - let x = 40; - let y = 110; - unsafe { - screen.blit_unchecked(BlitMethod::SolidFlippedBlended { horizontal_flip: false, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, x+16, y+48); - screen.blit_unchecked(BlitMethod::SolidFlippedBlended { horizontal_flip: true, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, x+80, y+48); - screen.blit_unchecked(BlitMethod::SolidFlippedBlended { horizontal_flip: false, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, x+144, y+48); - screen.blit_unchecked(BlitMethod::SolidFlippedBlended { horizontal_flip: true, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, x+208, y+48); - } + let x = 40; + let y = 110; + unsafe { + screen.blit_unchecked(BlitMethod::SolidFlippedBlended { horizontal_flip: false, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, x + 16, y + 48); + screen.blit_unchecked(BlitMethod::SolidFlippedBlended { horizontal_flip: true, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, x + 80, y + 48); + screen.blit_unchecked(BlitMethod::SolidFlippedBlended { horizontal_flip: false, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, x + 144, y + 48); + screen.blit_unchecked(BlitMethod::SolidFlippedBlended { horizontal_flip: true, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, x + 208, y + 48); + } - ////// + ////// - screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: false, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, -3, 46); - screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: true, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, -4, 76); - screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: false, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, -8, 106); - screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: true, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, -12, 136); - screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: false, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, -13, 166); - screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: true, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, -14, 196); - screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: false, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, -16, 226); + screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: false, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, -3, 46); + screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: true, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, -4, 76); + screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: false, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, -8, 106); + screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: true, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, -12, 136); + screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: false, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, -13, 166); + screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: true, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, -14, 196); + screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: false, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, -16, 226); - screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: false, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 46, -3); - screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: true, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 76, -4); - screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: false, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, 106, -8); - screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: true, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, 136, -12); - screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: false, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 166, -13); - screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: true, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 196, -14); - screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: false, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, 226, -16); + screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: false, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 46, -3); + screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: true, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 76, -4); + screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: false, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, 106, -8); + screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: true, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, 136, -12); + screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: false, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 166, -13); + screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: true, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 196, -14); + screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: false, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, 226, -16); - screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: false, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 307, 46); - screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: true, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 308, 76); - screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: false, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, 312, 106); - screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: true, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, 316, 136); - screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: false, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 317, 166); - screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: true, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 318, 196); - screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: false, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, 320, 226); + screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: false, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 307, 46); + screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: true, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 308, 76); + screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: false, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, 312, 106); + screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: true, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, 316, 136); + screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: false, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 317, 166); + screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: true, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 318, 196); + screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: false, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, 320, 226); - screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: false, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 46, 227); - screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: true, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 76, 228); - screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: false, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, 106, 232); - screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: true, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, 136, 236); - screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: false, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 166, 237); - screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: true, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 196, 238); - screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: false, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, 226, 240); + screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: false, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 46, 227); + screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: true, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 76, 228); + screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: false, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, 106, 232); + screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: true, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, 136, 236); + screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: false, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 166, 237); + screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: true, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 196, 238); + screen.blit(BlitMethod::SolidFlippedBlended { horizontal_flip: false, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, 226, 240); - let path = Path::new("tests/ref/blended_solid_flipped_blits.pcx"); - //screen.to_pcx_file(path, &palette).unwrap(); - assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); + let path = Path::new("tests/ref/blended_solid_flipped_blits.pcx"); + //screen.to_pcx_file(path, &palette).unwrap(); + assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); } #[test] fn solid_offset_blits() { - let (mut screen, palette) = setup(); - screen.clear(247); + let (mut screen, palette) = setup(); + screen.clear(247); - let bmp = generate_bitmap(16, 16); + let bmp = generate_bitmap(16, 16); - let x = 40; - let y = 20; - screen.blit(BlitMethod::SolidOffset(0), &bmp, x+16, y+48); - screen.blit(BlitMethod::SolidOffset(4), &bmp, x+80, y+48); - screen.blit(BlitMethod::SolidOffset(7), &bmp, x+144, y+48); - screen.blit(BlitMethod::SolidOffset(13), &bmp, x+208, y+48); + let x = 40; + let y = 20; + screen.blit(BlitMethod::SolidOffset(0), &bmp, x + 16, y + 48); + screen.blit(BlitMethod::SolidOffset(4), &bmp, x + 80, y + 48); + screen.blit(BlitMethod::SolidOffset(7), &bmp, x + 144, y + 48); + screen.blit(BlitMethod::SolidOffset(13), &bmp, x + 208, y + 48); - let x = 40; - let y = 110; - unsafe { - screen.blit_unchecked(BlitMethod::SolidOffset(0), &bmp, x+16, y+48); - screen.blit_unchecked(BlitMethod::SolidOffset(4), &bmp, x+80, y+48); - screen.blit_unchecked(BlitMethod::SolidOffset(7), &bmp, x+144, y+48); - screen.blit_unchecked(BlitMethod::SolidOffset(13), &bmp, x+208, y+48); - } + let x = 40; + let y = 110; + unsafe { + screen.blit_unchecked(BlitMethod::SolidOffset(0), &bmp, x + 16, y + 48); + screen.blit_unchecked(BlitMethod::SolidOffset(4), &bmp, x + 80, y + 48); + screen.blit_unchecked(BlitMethod::SolidOffset(7), &bmp, x + 144, y + 48); + screen.blit_unchecked(BlitMethod::SolidOffset(13), &bmp, x + 208, y + 48); + } - ////// + ////// - screen.blit(BlitMethod::SolidOffset(3), &bmp, -3, 46); - screen.blit(BlitMethod::SolidOffset(3), &bmp, -4, 76); - screen.blit(BlitMethod::SolidOffset(3), &bmp, -8, 106); - screen.blit(BlitMethod::SolidOffset(3), &bmp, -12, 136); - screen.blit(BlitMethod::SolidOffset(3), &bmp, -13, 166); - screen.blit(BlitMethod::SolidOffset(3), &bmp, -14, 196); - screen.blit(BlitMethod::SolidOffset(3), &bmp, -16, 226); + screen.blit(BlitMethod::SolidOffset(3), &bmp, -3, 46); + screen.blit(BlitMethod::SolidOffset(3), &bmp, -4, 76); + screen.blit(BlitMethod::SolidOffset(3), &bmp, -8, 106); + screen.blit(BlitMethod::SolidOffset(3), &bmp, -12, 136); + screen.blit(BlitMethod::SolidOffset(3), &bmp, -13, 166); + screen.blit(BlitMethod::SolidOffset(3), &bmp, -14, 196); + screen.blit(BlitMethod::SolidOffset(3), &bmp, -16, 226); - screen.blit(BlitMethod::SolidOffset(8), &bmp, 46, -3); - screen.blit(BlitMethod::SolidOffset(8), &bmp, 76, -4); - screen.blit(BlitMethod::SolidOffset(8), &bmp, 106, -8); - screen.blit(BlitMethod::SolidOffset(8), &bmp, 136, -12); - screen.blit(BlitMethod::SolidOffset(8), &bmp, 166, -13); - screen.blit(BlitMethod::SolidOffset(8), &bmp, 196, -14); - screen.blit(BlitMethod::SolidOffset(8), &bmp, 226, -16); + screen.blit(BlitMethod::SolidOffset(8), &bmp, 46, -3); + screen.blit(BlitMethod::SolidOffset(8), &bmp, 76, -4); + screen.blit(BlitMethod::SolidOffset(8), &bmp, 106, -8); + screen.blit(BlitMethod::SolidOffset(8), &bmp, 136, -12); + screen.blit(BlitMethod::SolidOffset(8), &bmp, 166, -13); + screen.blit(BlitMethod::SolidOffset(8), &bmp, 196, -14); + screen.blit(BlitMethod::SolidOffset(8), &bmp, 226, -16); - screen.blit(BlitMethod::SolidOffset(15), &bmp, 307, 46); - screen.blit(BlitMethod::SolidOffset(15), &bmp, 308, 76); - screen.blit(BlitMethod::SolidOffset(15), &bmp, 312, 106); - screen.blit(BlitMethod::SolidOffset(15), &bmp, 316, 136); - screen.blit(BlitMethod::SolidOffset(15), &bmp, 317, 166); - screen.blit(BlitMethod::SolidOffset(15), &bmp, 318, 196); - screen.blit(BlitMethod::SolidOffset(15), &bmp, 320, 226); + screen.blit(BlitMethod::SolidOffset(15), &bmp, 307, 46); + screen.blit(BlitMethod::SolidOffset(15), &bmp, 308, 76); + screen.blit(BlitMethod::SolidOffset(15), &bmp, 312, 106); + screen.blit(BlitMethod::SolidOffset(15), &bmp, 316, 136); + screen.blit(BlitMethod::SolidOffset(15), &bmp, 317, 166); + screen.blit(BlitMethod::SolidOffset(15), &bmp, 318, 196); + screen.blit(BlitMethod::SolidOffset(15), &bmp, 320, 226); - screen.blit(BlitMethod::SolidOffset(22), &bmp, 46, 227); - screen.blit(BlitMethod::SolidOffset(22), &bmp, 76, 228); - screen.blit(BlitMethod::SolidOffset(22), &bmp, 106, 232); - screen.blit(BlitMethod::SolidOffset(22), &bmp, 136, 236); - screen.blit(BlitMethod::SolidOffset(22), &bmp, 166, 237); - screen.blit(BlitMethod::SolidOffset(22), &bmp, 196, 238); - screen.blit(BlitMethod::SolidOffset(22), &bmp, 226, 240); + screen.blit(BlitMethod::SolidOffset(22), &bmp, 46, 227); + screen.blit(BlitMethod::SolidOffset(22), &bmp, 76, 228); + screen.blit(BlitMethod::SolidOffset(22), &bmp, 106, 232); + screen.blit(BlitMethod::SolidOffset(22), &bmp, 136, 236); + screen.blit(BlitMethod::SolidOffset(22), &bmp, 166, 237); + screen.blit(BlitMethod::SolidOffset(22), &bmp, 196, 238); + screen.blit(BlitMethod::SolidOffset(22), &bmp, 226, 240); - let path = Path::new("tests/ref/solid_offset_blits.pcx"); - //screen.to_pcx_file(path, &palette).unwrap(); - assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); + let path = Path::new("tests/ref/solid_offset_blits.pcx"); + //screen.to_pcx_file(path, &palette).unwrap(); + assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); } #[test] fn solid_flipped_offset_blits() { - let (mut screen, palette) = setup(); - screen.clear(247); + let (mut screen, palette) = setup(); + screen.clear(247); - let bmp = generate_bitmap(16, 16); + let bmp = generate_bitmap(16, 16); - let x = 40; - let y = 20; - screen.blit(BlitMethod::SolidFlippedOffset { offset: 0, horizontal_flip: false, vertical_flip: false }, &bmp, x+16, y+48); - screen.blit(BlitMethod::SolidFlippedOffset { offset: 4, horizontal_flip: true, vertical_flip: false }, &bmp, x+80, y+48); - screen.blit(BlitMethod::SolidFlippedOffset { offset: 7, horizontal_flip: false, vertical_flip: true }, &bmp, x+144, y+48); - screen.blit(BlitMethod::SolidFlippedOffset { offset: 13, horizontal_flip: true, vertical_flip: true }, &bmp, x+208, y+48); + let x = 40; + let y = 20; + screen.blit(BlitMethod::SolidFlippedOffset { offset: 0, horizontal_flip: false, vertical_flip: false }, &bmp, x + 16, y + 48); + screen.blit(BlitMethod::SolidFlippedOffset { offset: 4, horizontal_flip: true, vertical_flip: false }, &bmp, x + 80, y + 48); + screen.blit(BlitMethod::SolidFlippedOffset { offset: 7, horizontal_flip: false, vertical_flip: true }, &bmp, x + 144, y + 48); + screen.blit(BlitMethod::SolidFlippedOffset { offset: 13, horizontal_flip: true, vertical_flip: true }, &bmp, x + 208, y + 48); - let x = 40; - let y = 110; - unsafe { - screen.blit_unchecked(BlitMethod::SolidFlippedOffset { offset: 0, horizontal_flip: false, vertical_flip: false }, &bmp, x+16, y+48); - screen.blit_unchecked(BlitMethod::SolidFlippedOffset { offset: 4, horizontal_flip: true, vertical_flip: false }, &bmp, x+80, y+48); - screen.blit_unchecked(BlitMethod::SolidFlippedOffset { offset: 7, horizontal_flip: false, vertical_flip: true }, &bmp, x+144, y+48); - screen.blit_unchecked(BlitMethod::SolidFlippedOffset { offset: 13, horizontal_flip: true, vertical_flip: true }, &bmp, x+208, y+48); - } + let x = 40; + let y = 110; + unsafe { + screen.blit_unchecked(BlitMethod::SolidFlippedOffset { offset: 0, horizontal_flip: false, vertical_flip: false }, &bmp, x + 16, y + 48); + screen.blit_unchecked(BlitMethod::SolidFlippedOffset { offset: 4, horizontal_flip: true, vertical_flip: false }, &bmp, x + 80, y + 48); + screen.blit_unchecked(BlitMethod::SolidFlippedOffset { offset: 7, horizontal_flip: false, vertical_flip: true }, &bmp, x + 144, y + 48); + screen.blit_unchecked(BlitMethod::SolidFlippedOffset { offset: 13, horizontal_flip: true, vertical_flip: true }, &bmp, x + 208, y + 48); + } - ////// + ////// - screen.blit(BlitMethod::SolidFlippedOffset { offset: 3, horizontal_flip: false, vertical_flip: false }, &bmp, -3, 46); - screen.blit(BlitMethod::SolidFlippedOffset { offset: 3, horizontal_flip: true, vertical_flip: false }, &bmp, -4, 76); - screen.blit(BlitMethod::SolidFlippedOffset { offset: 3, horizontal_flip: false, vertical_flip: true }, &bmp, -8, 106); - screen.blit(BlitMethod::SolidFlippedOffset { offset: 3, horizontal_flip: true, vertical_flip: true }, &bmp, -12, 136); - screen.blit(BlitMethod::SolidFlippedOffset { offset: 3, horizontal_flip: false, vertical_flip: true }, &bmp, -13, 166); - screen.blit(BlitMethod::SolidFlippedOffset { offset: 3, horizontal_flip: true, vertical_flip: false }, &bmp, -14, 196); - screen.blit(BlitMethod::SolidFlippedOffset { offset: 3, horizontal_flip: false, vertical_flip: true }, &bmp, -16, 226); + screen.blit(BlitMethod::SolidFlippedOffset { offset: 3, horizontal_flip: false, vertical_flip: false }, &bmp, -3, 46); + screen.blit(BlitMethod::SolidFlippedOffset { offset: 3, horizontal_flip: true, vertical_flip: false }, &bmp, -4, 76); + screen.blit(BlitMethod::SolidFlippedOffset { offset: 3, horizontal_flip: false, vertical_flip: true }, &bmp, -8, 106); + screen.blit(BlitMethod::SolidFlippedOffset { offset: 3, horizontal_flip: true, vertical_flip: true }, &bmp, -12, 136); + screen.blit(BlitMethod::SolidFlippedOffset { offset: 3, horizontal_flip: false, vertical_flip: true }, &bmp, -13, 166); + screen.blit(BlitMethod::SolidFlippedOffset { offset: 3, horizontal_flip: true, vertical_flip: false }, &bmp, -14, 196); + screen.blit(BlitMethod::SolidFlippedOffset { offset: 3, horizontal_flip: false, vertical_flip: true }, &bmp, -16, 226); - screen.blit(BlitMethod::SolidFlippedOffset { offset: 8, horizontal_flip: false, vertical_flip: false }, &bmp, 46, -3); - screen.blit(BlitMethod::SolidFlippedOffset { offset: 8, horizontal_flip: true, vertical_flip: false }, &bmp, 76, -4); - screen.blit(BlitMethod::SolidFlippedOffset { offset: 8, horizontal_flip: false, vertical_flip: true }, &bmp, 106, -8); - screen.blit(BlitMethod::SolidFlippedOffset { offset: 8, horizontal_flip: true, vertical_flip: true }, &bmp, 136, -12); - screen.blit(BlitMethod::SolidFlippedOffset { offset: 8, horizontal_flip: false, vertical_flip: false }, &bmp, 166, -13); - screen.blit(BlitMethod::SolidFlippedOffset { offset: 8, horizontal_flip: true, vertical_flip: false }, &bmp, 196, -14); - screen.blit(BlitMethod::SolidFlippedOffset { offset: 8, horizontal_flip: false, vertical_flip: true }, &bmp, 226, -16); + screen.blit(BlitMethod::SolidFlippedOffset { offset: 8, horizontal_flip: false, vertical_flip: false }, &bmp, 46, -3); + screen.blit(BlitMethod::SolidFlippedOffset { offset: 8, horizontal_flip: true, vertical_flip: false }, &bmp, 76, -4); + screen.blit(BlitMethod::SolidFlippedOffset { offset: 8, horizontal_flip: false, vertical_flip: true }, &bmp, 106, -8); + screen.blit(BlitMethod::SolidFlippedOffset { offset: 8, horizontal_flip: true, vertical_flip: true }, &bmp, 136, -12); + screen.blit(BlitMethod::SolidFlippedOffset { offset: 8, horizontal_flip: false, vertical_flip: false }, &bmp, 166, -13); + screen.blit(BlitMethod::SolidFlippedOffset { offset: 8, horizontal_flip: true, vertical_flip: false }, &bmp, 196, -14); + screen.blit(BlitMethod::SolidFlippedOffset { offset: 8, horizontal_flip: false, vertical_flip: true }, &bmp, 226, -16); - screen.blit(BlitMethod::SolidFlippedOffset { offset: 15, horizontal_flip: false, vertical_flip: false }, &bmp, 307, 46); - screen.blit(BlitMethod::SolidFlippedOffset { offset: 15, horizontal_flip: true, vertical_flip: false }, &bmp, 308, 76); - screen.blit(BlitMethod::SolidFlippedOffset { offset: 15, horizontal_flip: false, vertical_flip: true }, &bmp, 312, 106); - screen.blit(BlitMethod::SolidFlippedOffset { offset: 15, horizontal_flip: true, vertical_flip: true }, &bmp, 316, 136); - screen.blit(BlitMethod::SolidFlippedOffset { offset: 15, horizontal_flip: false, vertical_flip: false }, &bmp, 317, 166); - screen.blit(BlitMethod::SolidFlippedOffset { offset: 15, horizontal_flip: true, vertical_flip: false }, &bmp, 318, 196); - screen.blit(BlitMethod::SolidFlippedOffset { offset: 15, horizontal_flip: false, vertical_flip: true }, &bmp, 320, 226); + screen.blit(BlitMethod::SolidFlippedOffset { offset: 15, horizontal_flip: false, vertical_flip: false }, &bmp, 307, 46); + screen.blit(BlitMethod::SolidFlippedOffset { offset: 15, horizontal_flip: true, vertical_flip: false }, &bmp, 308, 76); + screen.blit(BlitMethod::SolidFlippedOffset { offset: 15, horizontal_flip: false, vertical_flip: true }, &bmp, 312, 106); + screen.blit(BlitMethod::SolidFlippedOffset { offset: 15, horizontal_flip: true, vertical_flip: true }, &bmp, 316, 136); + screen.blit(BlitMethod::SolidFlippedOffset { offset: 15, horizontal_flip: false, vertical_flip: false }, &bmp, 317, 166); + screen.blit(BlitMethod::SolidFlippedOffset { offset: 15, horizontal_flip: true, vertical_flip: false }, &bmp, 318, 196); + screen.blit(BlitMethod::SolidFlippedOffset { offset: 15, horizontal_flip: false, vertical_flip: true }, &bmp, 320, 226); - screen.blit(BlitMethod::SolidFlippedOffset { offset: 22, horizontal_flip: false, vertical_flip: false }, &bmp, 46, 227); - screen.blit(BlitMethod::SolidFlippedOffset { offset: 22, horizontal_flip: true, vertical_flip: false }, &bmp, 76, 228); - screen.blit(BlitMethod::SolidFlippedOffset { offset: 22, horizontal_flip: false, vertical_flip: true }, &bmp, 106, 232); - screen.blit(BlitMethod::SolidFlippedOffset { offset: 22, horizontal_flip: true, vertical_flip: true }, &bmp, 136, 236); - screen.blit(BlitMethod::SolidFlippedOffset { offset: 22, horizontal_flip: false, vertical_flip: false }, &bmp, 166, 237); - screen.blit(BlitMethod::SolidFlippedOffset { offset: 22, horizontal_flip: true, vertical_flip: false }, &bmp, 196, 238); - screen.blit(BlitMethod::SolidFlippedOffset { offset: 22, horizontal_flip: false, vertical_flip: true }, &bmp, 226, 240); + screen.blit(BlitMethod::SolidFlippedOffset { offset: 22, horizontal_flip: false, vertical_flip: false }, &bmp, 46, 227); + screen.blit(BlitMethod::SolidFlippedOffset { offset: 22, horizontal_flip: true, vertical_flip: false }, &bmp, 76, 228); + screen.blit(BlitMethod::SolidFlippedOffset { offset: 22, horizontal_flip: false, vertical_flip: true }, &bmp, 106, 232); + screen.blit(BlitMethod::SolidFlippedOffset { offset: 22, horizontal_flip: true, vertical_flip: true }, &bmp, 136, 236); + screen.blit(BlitMethod::SolidFlippedOffset { offset: 22, horizontal_flip: false, vertical_flip: false }, &bmp, 166, 237); + screen.blit(BlitMethod::SolidFlippedOffset { offset: 22, horizontal_flip: true, vertical_flip: false }, &bmp, 196, 238); + screen.blit(BlitMethod::SolidFlippedOffset { offset: 22, horizontal_flip: false, vertical_flip: true }, &bmp, 226, 240); - let path = Path::new("tests/ref/solid_flipped_offset_blits.pcx"); - //screen.to_pcx_file(path, &palette).unwrap(); - assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); + let path = Path::new("tests/ref/solid_flipped_offset_blits.pcx"); + //screen.to_pcx_file(path, &palette).unwrap(); + assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); } #[test] fn transparent_blits() { - let (mut screen, palette) = setup(); - screen.clear(247); + let (mut screen, palette) = setup(); + screen.clear(247); - let bmp16 = generate_bitmap(16, 16); - let bmp12 = generate_bitmap(12, 12); - let bmp21 = generate_bitmap(21, 21); - let bmp3 = generate_bitmap(3, 3); + let bmp16 = generate_bitmap(16, 16); + let bmp12 = generate_bitmap(12, 12); + let bmp21 = generate_bitmap(21, 21); + let bmp3 = generate_bitmap(3, 3); - let x = 40; - let y = 20; - screen.blit(BlitMethod::Transparent(0), &bmp16, x+16, y+48); - screen.blit(BlitMethod::Transparent(0), &bmp12, x+80, y+48); - screen.blit(BlitMethod::Transparent(0), &bmp21, x+144, y+48); - screen.blit(BlitMethod::Transparent(0), &bmp3, x+208, y+48); + let x = 40; + let y = 20; + screen.blit(BlitMethod::Transparent(0), &bmp16, x + 16, y + 48); + screen.blit(BlitMethod::Transparent(0), &bmp12, x + 80, y + 48); + screen.blit(BlitMethod::Transparent(0), &bmp21, x + 144, y + 48); + screen.blit(BlitMethod::Transparent(0), &bmp3, x + 208, y + 48); - let x = 40; - let y = 110; - unsafe { - screen.blit_unchecked(BlitMethod::Transparent(0), &bmp16, x+16, y+48); - screen.blit_unchecked(BlitMethod::Transparent(0), &bmp12, x+80, y+48); - screen.blit_unchecked(BlitMethod::Transparent(0), &bmp21, x+144, y+48); - screen.blit_unchecked(BlitMethod::Transparent(0), &bmp3, x+208, y+48); - } + let x = 40; + let y = 110; + unsafe { + screen.blit_unchecked(BlitMethod::Transparent(0), &bmp16, x + 16, y + 48); + screen.blit_unchecked(BlitMethod::Transparent(0), &bmp12, x + 80, y + 48); + screen.blit_unchecked(BlitMethod::Transparent(0), &bmp21, x + 144, y + 48); + screen.blit_unchecked(BlitMethod::Transparent(0), &bmp3, x + 208, y + 48); + } - ////// + ////// - screen.blit(BlitMethod::Transparent(0), &bmp16, -3, 46); - screen.blit(BlitMethod::Transparent(0), &bmp16, -4, 76); - screen.blit(BlitMethod::Transparent(0), &bmp16, -8, 106); - screen.blit(BlitMethod::Transparent(0), &bmp16, -12, 136); - screen.blit(BlitMethod::Transparent(0), &bmp16, -13, 166); - screen.blit(BlitMethod::Transparent(0), &bmp16, -14, 196); - screen.blit(BlitMethod::Transparent(0), &bmp16, -16, 226); + screen.blit(BlitMethod::Transparent(0), &bmp16, -3, 46); + screen.blit(BlitMethod::Transparent(0), &bmp16, -4, 76); + screen.blit(BlitMethod::Transparent(0), &bmp16, -8, 106); + screen.blit(BlitMethod::Transparent(0), &bmp16, -12, 136); + screen.blit(BlitMethod::Transparent(0), &bmp16, -13, 166); + screen.blit(BlitMethod::Transparent(0), &bmp16, -14, 196); + screen.blit(BlitMethod::Transparent(0), &bmp16, -16, 226); - screen.blit(BlitMethod::Transparent(0), &bmp16, 46, -3); - screen.blit(BlitMethod::Transparent(0), &bmp16, 76, -4); - screen.blit(BlitMethod::Transparent(0), &bmp16, 106, -8); - screen.blit(BlitMethod::Transparent(0), &bmp16, 136, -12); - screen.blit(BlitMethod::Transparent(0), &bmp16, 166, -13); - screen.blit(BlitMethod::Transparent(0), &bmp16, 196, -14); - screen.blit(BlitMethod::Transparent(0), &bmp16, 226, -16); + screen.blit(BlitMethod::Transparent(0), &bmp16, 46, -3); + screen.blit(BlitMethod::Transparent(0), &bmp16, 76, -4); + screen.blit(BlitMethod::Transparent(0), &bmp16, 106, -8); + screen.blit(BlitMethod::Transparent(0), &bmp16, 136, -12); + screen.blit(BlitMethod::Transparent(0), &bmp16, 166, -13); + screen.blit(BlitMethod::Transparent(0), &bmp16, 196, -14); + screen.blit(BlitMethod::Transparent(0), &bmp16, 226, -16); - screen.blit(BlitMethod::Transparent(0), &bmp16, 307, 46); - screen.blit(BlitMethod::Transparent(0), &bmp16, 308, 76); - screen.blit(BlitMethod::Transparent(0), &bmp16, 312, 106); - screen.blit(BlitMethod::Transparent(0), &bmp16, 316, 136); - screen.blit(BlitMethod::Transparent(0), &bmp16, 317, 166); - screen.blit(BlitMethod::Transparent(0), &bmp16, 318, 196); - screen.blit(BlitMethod::Transparent(0), &bmp16, 320, 226); + screen.blit(BlitMethod::Transparent(0), &bmp16, 307, 46); + screen.blit(BlitMethod::Transparent(0), &bmp16, 308, 76); + screen.blit(BlitMethod::Transparent(0), &bmp16, 312, 106); + screen.blit(BlitMethod::Transparent(0), &bmp16, 316, 136); + screen.blit(BlitMethod::Transparent(0), &bmp16, 317, 166); + screen.blit(BlitMethod::Transparent(0), &bmp16, 318, 196); + screen.blit(BlitMethod::Transparent(0), &bmp16, 320, 226); - screen.blit(BlitMethod::Transparent(0), &bmp16, 46, 227); - screen.blit(BlitMethod::Transparent(0), &bmp16, 76, 228); - screen.blit(BlitMethod::Transparent(0), &bmp16, 106, 232); - screen.blit(BlitMethod::Transparent(0), &bmp16, 136, 236); - screen.blit(BlitMethod::Transparent(0), &bmp16, 166, 237); - screen.blit(BlitMethod::Transparent(0), &bmp16, 196, 238); - screen.blit(BlitMethod::Transparent(0), &bmp16, 226, 240); + screen.blit(BlitMethod::Transparent(0), &bmp16, 46, 227); + screen.blit(BlitMethod::Transparent(0), &bmp16, 76, 228); + screen.blit(BlitMethod::Transparent(0), &bmp16, 106, 232); + screen.blit(BlitMethod::Transparent(0), &bmp16, 136, 236); + screen.blit(BlitMethod::Transparent(0), &bmp16, 166, 237); + screen.blit(BlitMethod::Transparent(0), &bmp16, 196, 238); + screen.blit(BlitMethod::Transparent(0), &bmp16, 226, 240); - let path = Path::new("tests/ref/transparent_blits.pcx"); - //screen.to_pcx_file(path, &palette).unwrap(); - assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); + let path = Path::new("tests/ref/transparent_blits.pcx"); + //screen.to_pcx_file(path, &palette).unwrap(); + assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); } #[test] fn blended_transparent_blits() { - let (mut screen, palette, blend_map) = setup_for_blending(); - let blend_map = Rc::new(blend_map); + let (mut screen, palette, blend_map) = setup_for_blending(); + let blend_map = Rc::new(blend_map); - let bmp16 = generate_bitmap(16, 16); - let bmp12 = generate_bitmap(12, 12); - let bmp21 = generate_bitmap(21, 21); - let bmp3 = generate_bitmap(3, 3); + let bmp16 = generate_bitmap(16, 16); + let bmp12 = generate_bitmap(12, 12); + let bmp21 = generate_bitmap(21, 21); + let bmp3 = generate_bitmap(3, 3); - let x = 40; - let y = 20; - screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, x+16, y+48); - screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp12, x+80, y+48); - screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp21, x+144, y+48); - screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp3, x+208, y+48); + let x = 40; + let y = 20; + screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, x + 16, y + 48); + screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp12, x + 80, y + 48); + screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp21, x + 144, y + 48); + screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp3, x + 208, y + 48); - let x = 40; - let y = 110; - unsafe { - screen.blit_unchecked(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, x+16, y+48); - screen.blit_unchecked(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp12, x+80, y+48); - screen.blit_unchecked(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp21, x+144, y+48); - screen.blit_unchecked(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp3, x+208, y+48); - } + let x = 40; + let y = 110; + unsafe { + screen.blit_unchecked(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, x + 16, y + 48); + screen.blit_unchecked(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp12, x + 80, y + 48); + screen.blit_unchecked(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp21, x + 144, y + 48); + screen.blit_unchecked(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp3, x + 208, y + 48); + } - ////// + ////// - screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, -3, 46); - screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, -4, 76); - screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, -8, 106); - screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, -12, 136); - screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, -13, 166); - screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, -14, 196); - screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, -16, 226); + screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, -3, 46); + screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, -4, 76); + screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, -8, 106); + screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, -12, 136); + screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, -13, 166); + screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, -14, 196); + screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, -16, 226); - screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, 46, -3); - screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, 76, -4); - screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, 106, -8); - screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, 136, -12); - screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, 166, -13); - screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, 196, -14); - screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, 226, -16); + screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, 46, -3); + screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, 76, -4); + screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, 106, -8); + screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, 136, -12); + screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, 166, -13); + screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, 196, -14); + screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, 226, -16); - screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, 307, 46); - screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, 308, 76); - screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, 312, 106); - screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, 316, 136); - screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, 317, 166); - screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, 318, 196); - screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, 320, 226); + screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, 307, 46); + screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, 308, 76); + screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, 312, 106); + screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, 316, 136); + screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, 317, 166); + screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, 318, 196); + screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, 320, 226); - screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, 46, 227); - screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, 76, 228); - screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, 106, 232); - screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, 136, 236); - screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, 166, 237); - screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, 196, 238); - screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, 226, 240); + screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, 46, 227); + screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, 76, 228); + screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, 106, 232); + screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, 136, 236); + screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, 166, 237); + screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, 196, 238); + screen.blit(BlitMethod::TransparentBlended { transparent_color: 0, blend_map: blend_map.clone() }, &bmp16, 226, 240); - let path = Path::new("tests/ref/blended_transparent_blits.pcx"); - //screen.to_pcx_file(path, &palette).unwrap(); - assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); + let path = Path::new("tests/ref/blended_transparent_blits.pcx"); + //screen.to_pcx_file(path, &palette).unwrap(); + assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); } #[test] fn transparent_flipped_blits() { - let (mut screen, palette) = setup(); - screen.clear(247); + let (mut screen, palette) = setup(); + screen.clear(247); - let bmp = generate_bitmap(16, 16); + let bmp = generate_bitmap(16, 16); - let x = 40; - let y = 20; - screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: false, vertical_flip: false }, &bmp, x+16, y+48); - screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: true, vertical_flip: false }, &bmp, x+80, y+48); - screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: false, vertical_flip: true }, &bmp, x+144, y+48); - screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: true, vertical_flip: true }, &bmp, x+208, y+48); + let x = 40; + let y = 20; + screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: false, vertical_flip: false }, &bmp, x + 16, y + 48); + screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: true, vertical_flip: false }, &bmp, x + 80, y + 48); + screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: false, vertical_flip: true }, &bmp, x + 144, y + 48); + screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: true, vertical_flip: true }, &bmp, x + 208, y + 48); - let x = 40; - let y = 110; - unsafe { - screen.blit_unchecked(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: false, vertical_flip: false }, &bmp, x+16, y+48); - screen.blit_unchecked(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: true, vertical_flip: false }, &bmp, x+80, y+48); - screen.blit_unchecked(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: false, vertical_flip: true }, &bmp, x+144, y+48); - screen.blit_unchecked(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: true, vertical_flip: true }, &bmp, x+208, y+48); - } + let x = 40; + let y = 110; + unsafe { + screen.blit_unchecked(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: false, vertical_flip: false }, &bmp, x + 16, y + 48); + screen.blit_unchecked(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: true, vertical_flip: false }, &bmp, x + 80, y + 48); + screen.blit_unchecked(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: false, vertical_flip: true }, &bmp, x + 144, y + 48); + screen.blit_unchecked(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: true, vertical_flip: true }, &bmp, x + 208, y + 48); + } - ////// + ////// - screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: false, vertical_flip: false }, &bmp, -3, 46); - screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: true, vertical_flip: false }, &bmp, -4, 76); - screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: false, vertical_flip: true }, &bmp, -8, 106); - screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: true, vertical_flip: true }, &bmp, -12, 136); - screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: false, vertical_flip: true }, &bmp, -13, 166); - screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: true, vertical_flip: false }, &bmp, -14, 196); - screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: false, vertical_flip: true }, &bmp, -16, 226); + screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: false, vertical_flip: false }, &bmp, -3, 46); + screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: true, vertical_flip: false }, &bmp, -4, 76); + screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: false, vertical_flip: true }, &bmp, -8, 106); + screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: true, vertical_flip: true }, &bmp, -12, 136); + screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: false, vertical_flip: true }, &bmp, -13, 166); + screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: true, vertical_flip: false }, &bmp, -14, 196); + screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: false, vertical_flip: true }, &bmp, -16, 226); - screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: false, vertical_flip: false }, &bmp, 46, -3); - screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: true, vertical_flip: false }, &bmp, 76, -4); - screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: false, vertical_flip: true }, &bmp, 106, -8); - screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: true, vertical_flip: true }, &bmp, 136, -12); - screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: false, vertical_flip: false }, &bmp, 166, -13); - screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: true, vertical_flip: false }, &bmp, 196, -14); - screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: false, vertical_flip: true }, &bmp, 226, -16); + screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: false, vertical_flip: false }, &bmp, 46, -3); + screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: true, vertical_flip: false }, &bmp, 76, -4); + screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: false, vertical_flip: true }, &bmp, 106, -8); + screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: true, vertical_flip: true }, &bmp, 136, -12); + screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: false, vertical_flip: false }, &bmp, 166, -13); + screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: true, vertical_flip: false }, &bmp, 196, -14); + screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: false, vertical_flip: true }, &bmp, 226, -16); - screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: false, vertical_flip: false }, &bmp, 307, 46); - screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: true, vertical_flip: false }, &bmp, 308, 76); - screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: false, vertical_flip: true }, &bmp, 312, 106); - screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: true, vertical_flip: true }, &bmp, 316, 136); - screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: false, vertical_flip: false }, &bmp, 317, 166); - screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: true, vertical_flip: false }, &bmp, 318, 196); - screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: false, vertical_flip: true }, &bmp, 320, 226); + screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: false, vertical_flip: false }, &bmp, 307, 46); + screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: true, vertical_flip: false }, &bmp, 308, 76); + screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: false, vertical_flip: true }, &bmp, 312, 106); + screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: true, vertical_flip: true }, &bmp, 316, 136); + screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: false, vertical_flip: false }, &bmp, 317, 166); + screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: true, vertical_flip: false }, &bmp, 318, 196); + screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: false, vertical_flip: true }, &bmp, 320, 226); - screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: false, vertical_flip: false }, &bmp, 46, 227); - screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: true, vertical_flip: false }, &bmp, 76, 228); - screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: false, vertical_flip: true }, &bmp, 106, 232); - screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: true, vertical_flip: true }, &bmp, 136, 236); - screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: false, vertical_flip: false }, &bmp, 166, 237); - screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: true, vertical_flip: false }, &bmp, 196, 238); - screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: false, vertical_flip: true }, &bmp, 226, 240); + screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: false, vertical_flip: false }, &bmp, 46, 227); + screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: true, vertical_flip: false }, &bmp, 76, 228); + screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: false, vertical_flip: true }, &bmp, 106, 232); + screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: true, vertical_flip: true }, &bmp, 136, 236); + screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: false, vertical_flip: false }, &bmp, 166, 237); + screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: true, vertical_flip: false }, &bmp, 196, 238); + screen.blit(BlitMethod::TransparentFlipped { transparent_color: 0, horizontal_flip: false, vertical_flip: true }, &bmp, 226, 240); - let path = Path::new("tests/ref/transparent_flipped_blits.pcx"); - //screen.to_pcx_file(path, &palette).unwrap(); - assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); + let path = Path::new("tests/ref/transparent_flipped_blits.pcx"); + //screen.to_pcx_file(path, &palette).unwrap(); + assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); } #[test] fn blended_transparent_flipped_blits() { - let (mut screen, palette, blend_map) = setup_for_blending(); - let blend_map = Rc::new(blend_map); + let (mut screen, palette, blend_map) = setup_for_blending(); + let blend_map = Rc::new(blend_map); - let bmp = generate_bitmap(16, 16); + let bmp = generate_bitmap(16, 16); - let x = 40; - let y = 20; - screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: false, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, x+16, y+48); - screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: true, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, x+80, y+48); - screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: false, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, x+144, y+48); - screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: true, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, x+208, y+48); + let x = 40; + let y = 20; + screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: false, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, x + 16, y + 48); + screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: true, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, x + 80, y + 48); + screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: false, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, x + 144, y + 48); + screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: true, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, x + 208, y + 48); - let x = 40; - let y = 110; - unsafe { - screen.blit_unchecked(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: false, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, x+16, y+48); - screen.blit_unchecked(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: true, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, x+80, y+48); - screen.blit_unchecked(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: false, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, x+144, y+48); - screen.blit_unchecked(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: true, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, x+208, y+48); - } + let x = 40; + let y = 110; + unsafe { + screen.blit_unchecked(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: false, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, x + 16, y + 48); + screen.blit_unchecked(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: true, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, x + 80, y + 48); + screen.blit_unchecked(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: false, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, x + 144, y + 48); + screen.blit_unchecked(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: true, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, x + 208, y + 48); + } - ////// + ////// - screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: false, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, -3, 46); - screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: true, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, -4, 76); - screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: false, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, -8, 106); - screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: true, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, -12, 136); - screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: false, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, -13, 166); - screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: true, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, -14, 196); - screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: false, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, -16, 226); + screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: false, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, -3, 46); + screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: true, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, -4, 76); + screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: false, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, -8, 106); + screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: true, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, -12, 136); + screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: false, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, -13, 166); + screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: true, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, -14, 196); + screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: false, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, -16, 226); - screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: false, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 46, -3); - screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: true, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 76, -4); - screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: false, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, 106, -8); - screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: true, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, 136, -12); - screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: false, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 166, -13); - screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: true, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 196, -14); - screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: false, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, 226, -16); + screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: false, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 46, -3); + screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: true, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 76, -4); + screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: false, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, 106, -8); + screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: true, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, 136, -12); + screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: false, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 166, -13); + screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: true, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 196, -14); + screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: false, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, 226, -16); - screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: false, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 307, 46); - screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: true, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 308, 76); - screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: false, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, 312, 106); - screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: true, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, 316, 136); - screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: false, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 317, 166); - screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: true, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 318, 196); - screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: false, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, 320, 226); + screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: false, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 307, 46); + screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: true, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 308, 76); + screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: false, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, 312, 106); + screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: true, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, 316, 136); + screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: false, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 317, 166); + screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: true, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 318, 196); + screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: false, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, 320, 226); - screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: false, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 46, 227); - screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: true, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 76, 228); - screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: false, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, 106, 232); - screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: true, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, 136, 236); - screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: false, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 166, 237); - screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: true, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 196, 238); - screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: false, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, 226, 240); + screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: false, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 46, 227); + screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: true, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 76, 228); + screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: false, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, 106, 232); + screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: true, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, 136, 236); + screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: false, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 166, 237); + screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: true, vertical_flip: false, blend_map: blend_map.clone() }, &bmp, 196, 238); + screen.blit(BlitMethod::TransparentFlippedBlended { transparent_color: 0, horizontal_flip: false, vertical_flip: true, blend_map: blend_map.clone() }, &bmp, 226, 240); - let path = Path::new("tests/ref/blended_transparent_flipped_blits.pcx"); - //screen.to_pcx_file(path, &palette).unwrap(); - assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); + let path = Path::new("tests/ref/blended_transparent_flipped_blits.pcx"); + //screen.to_pcx_file(path, &palette).unwrap(); + assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); } #[test] fn transparent_offset_blits() { - let (mut screen, palette) = setup(); - screen.clear(247); + let (mut screen, palette) = setup(); + screen.clear(247); - let bmp = generate_bitmap(16, 16); + let bmp = generate_bitmap(16, 16); - let x = 40; - let y = 20; - screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 0 }, &bmp, x+16, y+48); - screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 4 }, &bmp, x+80, y+48); - screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 7 }, &bmp, x+144, y+48); - screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 13 }, &bmp, x+208, y+48); + let x = 40; + let y = 20; + screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 0 }, &bmp, x + 16, y + 48); + screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 4 }, &bmp, x + 80, y + 48); + screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 7 }, &bmp, x + 144, y + 48); + screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 13 }, &bmp, x + 208, y + 48); - let x = 40; - let y = 110; - unsafe { - screen.blit_unchecked(BlitMethod::TransparentOffset { transparent_color: 0, offset: 0 }, &bmp, x+16, y+48); - screen.blit_unchecked(BlitMethod::TransparentOffset { transparent_color: 0, offset: 4 }, &bmp, x+80, y+48); - screen.blit_unchecked(BlitMethod::TransparentOffset { transparent_color: 0, offset: 7 }, &bmp, x+144, y+48); - screen.blit_unchecked(BlitMethod::TransparentOffset { transparent_color: 0, offset: 13 }, &bmp, x+208, y+48); - } + let x = 40; + let y = 110; + unsafe { + screen.blit_unchecked(BlitMethod::TransparentOffset { transparent_color: 0, offset: 0 }, &bmp, x + 16, y + 48); + screen.blit_unchecked(BlitMethod::TransparentOffset { transparent_color: 0, offset: 4 }, &bmp, x + 80, y + 48); + screen.blit_unchecked(BlitMethod::TransparentOffset { transparent_color: 0, offset: 7 }, &bmp, x + 144, y + 48); + screen.blit_unchecked(BlitMethod::TransparentOffset { transparent_color: 0, offset: 13 }, &bmp, x + 208, y + 48); + } - ////// + ////// - screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 3 }, &bmp, -3, 46); - screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 3 }, &bmp, -4, 76); - screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 3 }, &bmp, -8, 106); - screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 3 }, &bmp, -12, 136); - screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 3 }, &bmp, -13, 166); - screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 3 }, &bmp, -14, 196); - screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 3 }, &bmp, -16, 226); + screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 3 }, &bmp, -3, 46); + screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 3 }, &bmp, -4, 76); + screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 3 }, &bmp, -8, 106); + screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 3 }, &bmp, -12, 136); + screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 3 }, &bmp, -13, 166); + screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 3 }, &bmp, -14, 196); + screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 3 }, &bmp, -16, 226); - screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 8 }, &bmp, 46, -3); - screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 8 }, &bmp, 76, -4); - screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 8 }, &bmp, 106, -8); - screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 8 }, &bmp, 136, -12); - screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 8 }, &bmp, 166, -13); - screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 8 }, &bmp, 196, -14); - screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 8 }, &bmp, 226, -16); + screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 8 }, &bmp, 46, -3); + screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 8 }, &bmp, 76, -4); + screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 8 }, &bmp, 106, -8); + screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 8 }, &bmp, 136, -12); + screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 8 }, &bmp, 166, -13); + screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 8 }, &bmp, 196, -14); + screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 8 }, &bmp, 226, -16); - screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 15 }, &bmp, 307, 46); - screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 15 }, &bmp, 308, 76); - screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 15 }, &bmp, 312, 106); - screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 15 }, &bmp, 316, 136); - screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 15 }, &bmp, 317, 166); - screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 15 }, &bmp, 318, 196); - screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 15 }, &bmp, 320, 226); + screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 15 }, &bmp, 307, 46); + screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 15 }, &bmp, 308, 76); + screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 15 }, &bmp, 312, 106); + screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 15 }, &bmp, 316, 136); + screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 15 }, &bmp, 317, 166); + screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 15 }, &bmp, 318, 196); + screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 15 }, &bmp, 320, 226); - screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 22 }, &bmp, 46, 227); - screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 22 }, &bmp, 76, 228); - screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 22 }, &bmp, 106, 232); - screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 22 }, &bmp, 136, 236); - screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 22 }, &bmp, 166, 237); - screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 22 }, &bmp, 196, 238); - screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 22 }, &bmp, 226, 240); + screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 22 }, &bmp, 46, 227); + screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 22 }, &bmp, 76, 228); + screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 22 }, &bmp, 106, 232); + screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 22 }, &bmp, 136, 236); + screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 22 }, &bmp, 166, 237); + screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 22 }, &bmp, 196, 238); + screen.blit(BlitMethod::TransparentOffset { transparent_color: 0, offset: 22 }, &bmp, 226, 240); - let path = Path::new("tests/ref/transparent_offset_blits.pcx"); - //screen.to_pcx_file(path, &palette).unwrap(); - assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); + let path = Path::new("tests/ref/transparent_offset_blits.pcx"); + //screen.to_pcx_file(path, &palette).unwrap(); + assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); } #[test] fn transparent_flipped_offset_blits() { - let (mut screen, palette) = setup(); - screen.clear(247); + let (mut screen, palette) = setup(); + screen.clear(247); - let bmp = generate_bitmap(16, 16); + let bmp = generate_bitmap(16, 16); - let x = 40; - let y = 20; - screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 0, horizontal_flip: false, vertical_flip: false }, &bmp, x+16, y+48); - screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 4, horizontal_flip: true, vertical_flip: false }, &bmp, x+80, y+48); - screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 7, horizontal_flip: false, vertical_flip: true }, &bmp, x+144, y+48); - screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 13, horizontal_flip: true, vertical_flip: true }, &bmp, x+208, y+48); + let x = 40; + let y = 20; + screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 0, horizontal_flip: false, vertical_flip: false }, &bmp, x + 16, y + 48); + screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 4, horizontal_flip: true, vertical_flip: false }, &bmp, x + 80, y + 48); + screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 7, horizontal_flip: false, vertical_flip: true }, &bmp, x + 144, y + 48); + screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 13, horizontal_flip: true, vertical_flip: true }, &bmp, x + 208, y + 48); - let x = 40; - let y = 110; - unsafe { - screen.blit_unchecked(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 0, horizontal_flip: false, vertical_flip: false }, &bmp, x+16, y+48); - screen.blit_unchecked(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 4, horizontal_flip: true, vertical_flip: false }, &bmp, x+80, y+48); - screen.blit_unchecked(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 7, horizontal_flip: false, vertical_flip: true }, &bmp, x+144, y+48); - screen.blit_unchecked(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 13, horizontal_flip: true, vertical_flip: true }, &bmp, x+208, y+48); - } + let x = 40; + let y = 110; + unsafe { + screen.blit_unchecked(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 0, horizontal_flip: false, vertical_flip: false }, &bmp, x + 16, y + 48); + screen.blit_unchecked(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 4, horizontal_flip: true, vertical_flip: false }, &bmp, x + 80, y + 48); + screen.blit_unchecked(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 7, horizontal_flip: false, vertical_flip: true }, &bmp, x + 144, y + 48); + screen.blit_unchecked(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 13, horizontal_flip: true, vertical_flip: true }, &bmp, x + 208, y + 48); + } - ////// + ////// - screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 3, horizontal_flip: false, vertical_flip: false }, &bmp, -3, 46); - screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 3, horizontal_flip: true, vertical_flip: false }, &bmp, -4, 76); - screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 3, horizontal_flip: false, vertical_flip: true }, &bmp, -8, 106); - screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 3, horizontal_flip: true, vertical_flip: true }, &bmp, -12, 136); - screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 3, horizontal_flip: false, vertical_flip: true }, &bmp, -13, 166); - screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 3, horizontal_flip: true, vertical_flip: false }, &bmp, -14, 196); - screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 3, horizontal_flip: false, vertical_flip: true }, &bmp, -16, 226); + screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 3, horizontal_flip: false, vertical_flip: false }, &bmp, -3, 46); + screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 3, horizontal_flip: true, vertical_flip: false }, &bmp, -4, 76); + screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 3, horizontal_flip: false, vertical_flip: true }, &bmp, -8, 106); + screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 3, horizontal_flip: true, vertical_flip: true }, &bmp, -12, 136); + screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 3, horizontal_flip: false, vertical_flip: true }, &bmp, -13, 166); + screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 3, horizontal_flip: true, vertical_flip: false }, &bmp, -14, 196); + screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 3, horizontal_flip: false, vertical_flip: true }, &bmp, -16, 226); - screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 8, horizontal_flip: false, vertical_flip: false }, &bmp, 46, -3); - screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 8, horizontal_flip: true, vertical_flip: false }, &bmp, 76, -4); - screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 8, horizontal_flip: false, vertical_flip: true }, &bmp, 106, -8); - screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 8, horizontal_flip: true, vertical_flip: true }, &bmp, 136, -12); - screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 8, horizontal_flip: false, vertical_flip: false }, &bmp, 166, -13); - screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 8, horizontal_flip: true, vertical_flip: false }, &bmp, 196, -14); - screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 8, horizontal_flip: false, vertical_flip: true }, &bmp, 226, -16); + screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 8, horizontal_flip: false, vertical_flip: false }, &bmp, 46, -3); + screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 8, horizontal_flip: true, vertical_flip: false }, &bmp, 76, -4); + screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 8, horizontal_flip: false, vertical_flip: true }, &bmp, 106, -8); + screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 8, horizontal_flip: true, vertical_flip: true }, &bmp, 136, -12); + screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 8, horizontal_flip: false, vertical_flip: false }, &bmp, 166, -13); + screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 8, horizontal_flip: true, vertical_flip: false }, &bmp, 196, -14); + screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 8, horizontal_flip: false, vertical_flip: true }, &bmp, 226, -16); - screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 15, horizontal_flip: false, vertical_flip: false }, &bmp, 307, 46); - screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 15, horizontal_flip: true, vertical_flip: false }, &bmp, 308, 76); - screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 15, horizontal_flip: false, vertical_flip: true }, &bmp, 312, 106); - screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 15, horizontal_flip: true, vertical_flip: true }, &bmp, 316, 136); - screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 15, horizontal_flip: false, vertical_flip: false }, &bmp, 317, 166); - screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 15, horizontal_flip: true, vertical_flip: false }, &bmp, 318, 196); - screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 15, horizontal_flip: false, vertical_flip: true }, &bmp, 320, 226); + screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 15, horizontal_flip: false, vertical_flip: false }, &bmp, 307, 46); + screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 15, horizontal_flip: true, vertical_flip: false }, &bmp, 308, 76); + screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 15, horizontal_flip: false, vertical_flip: true }, &bmp, 312, 106); + screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 15, horizontal_flip: true, vertical_flip: true }, &bmp, 316, 136); + screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 15, horizontal_flip: false, vertical_flip: false }, &bmp, 317, 166); + screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 15, horizontal_flip: true, vertical_flip: false }, &bmp, 318, 196); + screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 15, horizontal_flip: false, vertical_flip: true }, &bmp, 320, 226); - screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 22, horizontal_flip: false, vertical_flip: false }, &bmp, 46, 227); - screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 22, horizontal_flip: true, vertical_flip: false }, &bmp, 76, 228); - screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 22, horizontal_flip: false, vertical_flip: true }, &bmp, 106, 232); - screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 22, horizontal_flip: true, vertical_flip: true }, &bmp, 136, 236); - screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 22, horizontal_flip: false, vertical_flip: false }, &bmp, 166, 237); - screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 22, horizontal_flip: true, vertical_flip: false }, &bmp, 196, 238); - screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 22, horizontal_flip: false, vertical_flip: true }, &bmp, 226, 240); + screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 22, horizontal_flip: false, vertical_flip: false }, &bmp, 46, 227); + screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 22, horizontal_flip: true, vertical_flip: false }, &bmp, 76, 228); + screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 22, horizontal_flip: false, vertical_flip: true }, &bmp, 106, 232); + screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 22, horizontal_flip: true, vertical_flip: true }, &bmp, 136, 236); + screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 22, horizontal_flip: false, vertical_flip: false }, &bmp, 166, 237); + screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 22, horizontal_flip: true, vertical_flip: false }, &bmp, 196, 238); + screen.blit(BlitMethod::TransparentFlippedOffset { transparent_color: 0, offset: 22, horizontal_flip: false, vertical_flip: true }, &bmp, 226, 240); - let path = Path::new("tests/ref/transparent_flipped_offset_blits.pcx"); - //screen.to_pcx_file(path, &palette).unwrap(); - assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); + let path = Path::new("tests/ref/transparent_flipped_offset_blits.pcx"); + //screen.to_pcx_file(path, &palette).unwrap(); + assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); } #[test] fn transparent_single_blits() { - let (mut screen, palette) = setup(); - screen.clear(247); + let (mut screen, palette) = setup(); + screen.clear(247); - let bmp = generate_bitmap(16, 16); + let bmp = generate_bitmap(16, 16); - let x = 40; - let y = 20; - screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 1 }, &bmp, x+16, y+48); - screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 4 }, &bmp, x+80, y+48); - screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 7 }, &bmp, x+144, y+48); - screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 13 }, &bmp, x+208, y+48); + let x = 40; + let y = 20; + screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 1 }, &bmp, x + 16, y + 48); + screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 4 }, &bmp, x + 80, y + 48); + screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 7 }, &bmp, x + 144, y + 48); + screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 13 }, &bmp, x + 208, y + 48); - let x = 40; - let y = 110; - unsafe { - screen.blit_unchecked(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 1 }, &bmp, x+16, y+48); - screen.blit_unchecked(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 4 }, &bmp, x+80, y+48); - screen.blit_unchecked(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 7 }, &bmp, x+144, y+48); - screen.blit_unchecked(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 13 }, &bmp, x+208, y+48); - } + let x = 40; + let y = 110; + unsafe { + screen.blit_unchecked(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 1 }, &bmp, x + 16, y + 48); + screen.blit_unchecked(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 4 }, &bmp, x + 80, y + 48); + screen.blit_unchecked(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 7 }, &bmp, x + 144, y + 48); + screen.blit_unchecked(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 13 }, &bmp, x + 208, y + 48); + } - ////// + ////// - screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 3 }, &bmp, -3, 46); - screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 3 }, &bmp, -4, 76); - screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 3 }, &bmp, -8, 106); - screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 3 }, &bmp, -12, 136); - screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 3 }, &bmp, -13, 166); - screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 3 }, &bmp, -14, 196); - screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 3 }, &bmp, -16, 226); + screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 3 }, &bmp, -3, 46); + screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 3 }, &bmp, -4, 76); + screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 3 }, &bmp, -8, 106); + screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 3 }, &bmp, -12, 136); + screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 3 }, &bmp, -13, 166); + screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 3 }, &bmp, -14, 196); + screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 3 }, &bmp, -16, 226); - screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 8 }, &bmp, 46, -3); - screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 8 }, &bmp, 76, -4); - screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 8 }, &bmp, 106, -8); - screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 8 }, &bmp, 136, -12); - screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 8 }, &bmp, 166, -13); - screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 8 }, &bmp, 196, -14); - screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 8 }, &bmp, 226, -16); + screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 8 }, &bmp, 46, -3); + screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 8 }, &bmp, 76, -4); + screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 8 }, &bmp, 106, -8); + screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 8 }, &bmp, 136, -12); + screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 8 }, &bmp, 166, -13); + screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 8 }, &bmp, 196, -14); + screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 8 }, &bmp, 226, -16); - screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 15 }, &bmp, 307, 46); - screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 15 }, &bmp, 308, 76); - screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 15 }, &bmp, 312, 106); - screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 15 }, &bmp, 316, 136); - screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 15 }, &bmp, 317, 166); - screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 15 }, &bmp, 318, 196); - screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 15 }, &bmp, 320, 226); + screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 15 }, &bmp, 307, 46); + screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 15 }, &bmp, 308, 76); + screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 15 }, &bmp, 312, 106); + screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 15 }, &bmp, 316, 136); + screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 15 }, &bmp, 317, 166); + screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 15 }, &bmp, 318, 196); + screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 15 }, &bmp, 320, 226); - screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 22 }, &bmp, 46, 227); - screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 22 }, &bmp, 76, 228); - screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 22 }, &bmp, 106, 232); - screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 22 }, &bmp, 136, 236); - screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 22 }, &bmp, 166, 237); - screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 22 }, &bmp, 196, 238); - screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 22 }, &bmp, 226, 240); + screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 22 }, &bmp, 46, 227); + screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 22 }, &bmp, 76, 228); + screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 22 }, &bmp, 106, 232); + screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 22 }, &bmp, 136, 236); + screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 22 }, &bmp, 166, 237); + screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 22 }, &bmp, 196, 238); + screen.blit(BlitMethod::TransparentSingle { transparent_color: 0, draw_color: 22 }, &bmp, 226, 240); - let path = Path::new("tests/ref/transparent_single_blits.pcx"); - //screen.to_pcx_file(path, &palette).unwrap(); - assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); + let path = Path::new("tests/ref/transparent_single_blits.pcx"); + //screen.to_pcx_file(path, &palette).unwrap(); + assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); } #[test] fn transparent_flipped_single_blits() { - let (mut screen, palette) = setup(); - screen.clear(247); + let (mut screen, palette) = setup(); + screen.clear(247); - let bmp = generate_bitmap(16, 16); + let bmp = generate_bitmap(16, 16); - let x = 40; - let y = 20; - screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 1, horizontal_flip: false, vertical_flip: false }, &bmp, x+16, y+48); - screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 4, horizontal_flip: true, vertical_flip: false }, &bmp, x+80, y+48); - screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 7, horizontal_flip: false, vertical_flip: true }, &bmp, x+144, y+48); - screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 13, horizontal_flip: true, vertical_flip: true }, &bmp, x+208, y+48); + let x = 40; + let y = 20; + screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 1, horizontal_flip: false, vertical_flip: false }, &bmp, x + 16, y + 48); + screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 4, horizontal_flip: true, vertical_flip: false }, &bmp, x + 80, y + 48); + screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 7, horizontal_flip: false, vertical_flip: true }, &bmp, x + 144, y + 48); + screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 13, horizontal_flip: true, vertical_flip: true }, &bmp, x + 208, y + 48); - let x = 40; - let y = 110; - unsafe { - screen.blit_unchecked(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 1, horizontal_flip: false, vertical_flip: false }, &bmp, x+16, y+48); - screen.blit_unchecked(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 4, horizontal_flip: true, vertical_flip: false }, &bmp, x+80, y+48); - screen.blit_unchecked(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 7, horizontal_flip: false, vertical_flip: true }, &bmp, x+144, y+48); - screen.blit_unchecked(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 13, horizontal_flip: true, vertical_flip: true }, &bmp, x+208, y+48); - } + let x = 40; + let y = 110; + unsafe { + screen.blit_unchecked(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 1, horizontal_flip: false, vertical_flip: false }, &bmp, x + 16, y + 48); + screen.blit_unchecked(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 4, horizontal_flip: true, vertical_flip: false }, &bmp, x + 80, y + 48); + screen.blit_unchecked(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 7, horizontal_flip: false, vertical_flip: true }, &bmp, x + 144, y + 48); + screen.blit_unchecked(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 13, horizontal_flip: true, vertical_flip: true }, &bmp, x + 208, y + 48); + } - ////// + ////// - screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 3, horizontal_flip: false, vertical_flip: false }, &bmp, -3, 46); - screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 3, horizontal_flip: true, vertical_flip: false }, &bmp, -4, 76); - screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 3, horizontal_flip: false, vertical_flip: true }, &bmp, -8, 106); - screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 3, horizontal_flip: true, vertical_flip: true }, &bmp, -12, 136); - screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 3, horizontal_flip: false, vertical_flip: true }, &bmp, -13, 166); - screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 3, horizontal_flip: true, vertical_flip: false }, &bmp, -14, 196); - screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 3, horizontal_flip: false, vertical_flip: true }, &bmp, -16, 226); + screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 3, horizontal_flip: false, vertical_flip: false }, &bmp, -3, 46); + screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 3, horizontal_flip: true, vertical_flip: false }, &bmp, -4, 76); + screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 3, horizontal_flip: false, vertical_flip: true }, &bmp, -8, 106); + screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 3, horizontal_flip: true, vertical_flip: true }, &bmp, -12, 136); + screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 3, horizontal_flip: false, vertical_flip: true }, &bmp, -13, 166); + screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 3, horizontal_flip: true, vertical_flip: false }, &bmp, -14, 196); + screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 3, horizontal_flip: false, vertical_flip: true }, &bmp, -16, 226); - screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 8, horizontal_flip: false, vertical_flip: false }, &bmp, 46, -3); - screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 8, horizontal_flip: true, vertical_flip: false }, &bmp, 76, -4); - screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 8, horizontal_flip: false, vertical_flip: true }, &bmp, 106, -8); - screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 8, horizontal_flip: true, vertical_flip: true }, &bmp, 136, -12); - screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 8, horizontal_flip: false, vertical_flip: false }, &bmp, 166, -13); - screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 8, horizontal_flip: true, vertical_flip: false }, &bmp, 196, -14); - screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 8, horizontal_flip: false, vertical_flip: true }, &bmp, 226, -16); + screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 8, horizontal_flip: false, vertical_flip: false }, &bmp, 46, -3); + screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 8, horizontal_flip: true, vertical_flip: false }, &bmp, 76, -4); + screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 8, horizontal_flip: false, vertical_flip: true }, &bmp, 106, -8); + screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 8, horizontal_flip: true, vertical_flip: true }, &bmp, 136, -12); + screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 8, horizontal_flip: false, vertical_flip: false }, &bmp, 166, -13); + screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 8, horizontal_flip: true, vertical_flip: false }, &bmp, 196, -14); + screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 8, horizontal_flip: false, vertical_flip: true }, &bmp, 226, -16); - screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 15, horizontal_flip: false, vertical_flip: false }, &bmp, 307, 46); - screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 15, horizontal_flip: true, vertical_flip: false }, &bmp, 308, 76); - screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 15, horizontal_flip: false, vertical_flip: true }, &bmp, 312, 106); - screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 15, horizontal_flip: true, vertical_flip: true }, &bmp, 316, 136); - screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 15, horizontal_flip: false, vertical_flip: false }, &bmp, 317, 166); - screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 15, horizontal_flip: true, vertical_flip: false }, &bmp, 318, 196); - screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 15, horizontal_flip: false, vertical_flip: true }, &bmp, 320, 226); + screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 15, horizontal_flip: false, vertical_flip: false }, &bmp, 307, 46); + screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 15, horizontal_flip: true, vertical_flip: false }, &bmp, 308, 76); + screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 15, horizontal_flip: false, vertical_flip: true }, &bmp, 312, 106); + screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 15, horizontal_flip: true, vertical_flip: true }, &bmp, 316, 136); + screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 15, horizontal_flip: false, vertical_flip: false }, &bmp, 317, 166); + screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 15, horizontal_flip: true, vertical_flip: false }, &bmp, 318, 196); + screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 15, horizontal_flip: false, vertical_flip: true }, &bmp, 320, 226); - screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 22, horizontal_flip: false, vertical_flip: false }, &bmp, 46, 227); - screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 22, horizontal_flip: true, vertical_flip: false }, &bmp, 76, 228); - screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 22, horizontal_flip: false, vertical_flip: true }, &bmp, 106, 232); - screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 22, horizontal_flip: true, vertical_flip: true }, &bmp, 136, 236); - screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 22, horizontal_flip: false, vertical_flip: false }, &bmp, 166, 237); - screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 22, horizontal_flip: true, vertical_flip: false }, &bmp, 196, 238); - screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 22, horizontal_flip: false, vertical_flip: true }, &bmp, 226, 240); + screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 22, horizontal_flip: false, vertical_flip: false }, &bmp, 46, 227); + screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 22, horizontal_flip: true, vertical_flip: false }, &bmp, 76, 228); + screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 22, horizontal_flip: false, vertical_flip: true }, &bmp, 106, 232); + screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 22, horizontal_flip: true, vertical_flip: true }, &bmp, 136, 236); + screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 22, horizontal_flip: false, vertical_flip: false }, &bmp, 166, 237); + screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 22, horizontal_flip: true, vertical_flip: false }, &bmp, 196, 238); + screen.blit(BlitMethod::TransparentFlippedSingle { transparent_color: 0, draw_color: 22, horizontal_flip: false, vertical_flip: true }, &bmp, 226, 240); - let path = Path::new("tests/ref/transparent_flipped_single_blits.pcx"); - //screen.to_pcx_file(path, &palette).unwrap(); - assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); + let path = Path::new("tests/ref/transparent_flipped_single_blits.pcx"); + //screen.to_pcx_file(path, &palette).unwrap(); + assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); } #[test] fn rotozoom_blits() { - let (mut screen, palette) = setup(); - screen.clear(247); + let (mut screen, palette) = setup(); + screen.clear(247); - let bmp = generate_bitmap(16, 16); + let bmp = generate_bitmap(16, 16); - let x = 40; - let y = 20; - screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, x+16, y+48); - screen.blit(BlitMethod::RotoZoom { angle: 0.3, scale_x: 1.5, scale_y: 1.0 }, &bmp, x+80, y+48); - screen.blit(BlitMethod::RotoZoom { angle: 0.6, scale_x: 1.0, scale_y: 1.5 }, &bmp, x+144, y+48); - screen.blit(BlitMethod::RotoZoom { angle: 2.0, scale_x: 0.7, scale_y: 0.7 }, &bmp, x+208, y+48); + let x = 40; + let y = 20; + screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, x + 16, y + 48); + screen.blit(BlitMethod::RotoZoom { angle: 0.3, scale_x: 1.5, scale_y: 1.0 }, &bmp, x + 80, y + 48); + screen.blit(BlitMethod::RotoZoom { angle: 0.6, scale_x: 1.0, scale_y: 1.5 }, &bmp, x + 144, y + 48); + screen.blit(BlitMethod::RotoZoom { angle: 2.0, scale_x: 0.7, scale_y: 0.7 }, &bmp, x + 208, y + 48); - let x = 40; - let y = 110; - unsafe { - screen.blit_unchecked(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, x+16, y+48); - screen.blit_unchecked(BlitMethod::RotoZoom { angle: 0.3, scale_x: 1.5, scale_y: 1.0 }, &bmp, x+80, y+48); - screen.blit_unchecked(BlitMethod::RotoZoom { angle: 0.6, scale_x: 1.0, scale_y: 1.5 }, &bmp, x+144, y+48); - screen.blit_unchecked(BlitMethod::RotoZoom { angle: 2.0, scale_x: 0.7, scale_y: 0.7 }, &bmp, x+208, y+48); - } + let x = 40; + let y = 110; + unsafe { + screen.blit_unchecked(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, x + 16, y + 48); + screen.blit_unchecked(BlitMethod::RotoZoom { angle: 0.3, scale_x: 1.5, scale_y: 1.0 }, &bmp, x + 80, y + 48); + screen.blit_unchecked(BlitMethod::RotoZoom { angle: 0.6, scale_x: 1.0, scale_y: 1.5 }, &bmp, x + 144, y + 48); + screen.blit_unchecked(BlitMethod::RotoZoom { angle: 2.0, scale_x: 0.7, scale_y: 0.7 }, &bmp, x + 208, y + 48); + } - ////// + ////// - screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, -3, 46); - screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, -4, 76); - screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, -8, 106); - screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, -12, 136); - screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, -13, 166); - screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, -14, 196); - screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, -16, 226); + screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, -3, 46); + screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, -4, 76); + screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, -8, 106); + screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, -12, 136); + screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, -13, 166); + screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, -14, 196); + screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, -16, 226); - screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 46, -3); - screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 76, -4); - screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 106, -8); - screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 136, -12); - screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 166, -13); - screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 196, -14); - screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 226, -16); + screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 46, -3); + screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 76, -4); + screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 106, -8); + screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 136, -12); + screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 166, -13); + screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 196, -14); + screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 226, -16); - screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 307, 46); - screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 308, 76); - screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 312, 106); - screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 316, 136); - screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 317, 166); - screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 318, 196); - screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 320, 226); + screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 307, 46); + screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 308, 76); + screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 312, 106); + screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 316, 136); + screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 317, 166); + screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 318, 196); + screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 320, 226); - screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 46, 227); - screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 76, 228); - screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 106, 232); - screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 136, 236); - screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 166, 237); - screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 196, 238); - screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 226, 240); + screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 46, 227); + screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 76, 228); + screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 106, 232); + screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 136, 236); + screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 166, 237); + screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 196, 238); + screen.blit(BlitMethod::RotoZoom { angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 226, 240); - let path = Path::new("tests/ref/rotozoom_blits.pcx"); - //screen.to_pcx_file(path, &palette).unwrap(); - assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); + let path = Path::new("tests/ref/rotozoom_blits.pcx"); + //screen.to_pcx_file(path, &palette).unwrap(); + assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); } #[test] fn blended_rotozoom_blits() { - let (mut screen, palette, blend_map) = setup_for_blending(); - let blend_map = Rc::new(blend_map); + let (mut screen, palette, blend_map) = setup_for_blending(); + let blend_map = Rc::new(blend_map); - let bmp = generate_bitmap(16, 16); + let bmp = generate_bitmap(16, 16); - let x = 40; - let y = 20; - screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, x+16, y+48); - screen.blit(BlitMethod::RotoZoomBlended { angle: 0.3, scale_x: 1.5, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, x+80, y+48); - screen.blit(BlitMethod::RotoZoomBlended { angle: 0.6, scale_x: 1.0, scale_y: 1.5, blend_map: blend_map.clone() }, &bmp, x+144, y+48); - screen.blit(BlitMethod::RotoZoomBlended { angle: 2.0, scale_x: 0.7, scale_y: 0.7, blend_map: blend_map.clone() }, &bmp, x+208, y+48); + let x = 40; + let y = 20; + screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, x + 16, y + 48); + screen.blit(BlitMethod::RotoZoomBlended { angle: 0.3, scale_x: 1.5, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, x + 80, y + 48); + screen.blit(BlitMethod::RotoZoomBlended { angle: 0.6, scale_x: 1.0, scale_y: 1.5, blend_map: blend_map.clone() }, &bmp, x + 144, y + 48); + screen.blit(BlitMethod::RotoZoomBlended { angle: 2.0, scale_x: 0.7, scale_y: 0.7, blend_map: blend_map.clone() }, &bmp, x + 208, y + 48); - let x = 40; - let y = 110; - unsafe { - screen.blit_unchecked(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, x+16, y+48); - screen.blit_unchecked(BlitMethod::RotoZoomBlended { angle: 0.3, scale_x: 1.5, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, x+80, y+48); - screen.blit_unchecked(BlitMethod::RotoZoomBlended { angle: 0.6, scale_x: 1.0, scale_y: 1.5, blend_map: blend_map.clone() }, &bmp, x+144, y+48); - screen.blit_unchecked(BlitMethod::RotoZoomBlended { angle: 2.0, scale_x: 0.7, scale_y: 0.7, blend_map: blend_map.clone() }, &bmp, x+208, y+48); - } + let x = 40; + let y = 110; + unsafe { + screen.blit_unchecked(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, x + 16, y + 48); + screen.blit_unchecked(BlitMethod::RotoZoomBlended { angle: 0.3, scale_x: 1.5, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, x + 80, y + 48); + screen.blit_unchecked(BlitMethod::RotoZoomBlended { angle: 0.6, scale_x: 1.0, scale_y: 1.5, blend_map: blend_map.clone() }, &bmp, x + 144, y + 48); + screen.blit_unchecked(BlitMethod::RotoZoomBlended { angle: 2.0, scale_x: 0.7, scale_y: 0.7, blend_map: blend_map.clone() }, &bmp, x + 208, y + 48); + } - ////// + ////// - screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, -3, 46); - screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, -4, 76); - screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, -8, 106); - screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, -12, 136); - screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, -13, 166); - screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, -14, 196); - screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, -16, 226); + screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, -3, 46); + screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, -4, 76); + screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, -8, 106); + screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, -12, 136); + screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, -13, 166); + screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, -14, 196); + screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, -16, 226); - screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 46, -3); - screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 76, -4); - screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 106, -8); - screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 136, -12); - screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 166, -13); - screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 196, -14); - screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 226, -16); + screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 46, -3); + screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 76, -4); + screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 106, -8); + screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 136, -12); + screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 166, -13); + screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 196, -14); + screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 226, -16); - screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 307, 46); - screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 308, 76); - screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 312, 106); - screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 316, 136); - screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 317, 166); - screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 318, 196); - screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 320, 226); + screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 307, 46); + screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 308, 76); + screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 312, 106); + screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 316, 136); + screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 317, 166); + screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 318, 196); + screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 320, 226); - screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 46, 227); - screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 76, 228); - screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 106, 232); - screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 136, 236); - screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 166, 237); - screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 196, 238); - screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 226, 240); + screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 46, 227); + screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 76, 228); + screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 106, 232); + screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 136, 236); + screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 166, 237); + screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 196, 238); + screen.blit(BlitMethod::RotoZoomBlended { angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 226, 240); - let path = Path::new("tests/ref/blended_rotozoom_blits.pcx"); - //screen.to_pcx_file(path, &palette).unwrap(); - assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); + let path = Path::new("tests/ref/blended_rotozoom_blits.pcx"); + //screen.to_pcx_file(path, &palette).unwrap(); + assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); } #[test] fn rotozoom_offset_blits() { - let (mut screen, palette) = setup(); - screen.clear(247); + let (mut screen, palette) = setup(); + screen.clear(247); - let bmp = generate_bitmap(16, 16); + let bmp = generate_bitmap(16, 16); - let x = 40; - let y = 20; - screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 0 }, &bmp, x+16, y+48); - screen.blit(BlitMethod::RotoZoomOffset { angle: 0.3, scale_x: 1.5, scale_y: 1.0, offset: 4 }, &bmp, x+80, y+48); - screen.blit(BlitMethod::RotoZoomOffset { angle: 0.6, scale_x: 1.0, scale_y: 1.5, offset: 7 }, &bmp, x+144, y+48); - screen.blit(BlitMethod::RotoZoomOffset { angle: 2.0, scale_x: 0.7, scale_y: 0.7, offset: 13 }, &bmp, x+208, y+48); + let x = 40; + let y = 20; + screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 0 }, &bmp, x + 16, y + 48); + screen.blit(BlitMethod::RotoZoomOffset { angle: 0.3, scale_x: 1.5, scale_y: 1.0, offset: 4 }, &bmp, x + 80, y + 48); + screen.blit(BlitMethod::RotoZoomOffset { angle: 0.6, scale_x: 1.0, scale_y: 1.5, offset: 7 }, &bmp, x + 144, y + 48); + screen.blit(BlitMethod::RotoZoomOffset { angle: 2.0, scale_x: 0.7, scale_y: 0.7, offset: 13 }, &bmp, x + 208, y + 48); - let x = 40; - let y = 110; - unsafe { - screen.blit_unchecked(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 0 }, &bmp, x+16, y+48); - screen.blit_unchecked(BlitMethod::RotoZoomOffset { angle: 0.3, scale_x: 1.5, scale_y: 1.0, offset: 4 }, &bmp, x+80, y+48); - screen.blit_unchecked(BlitMethod::RotoZoomOffset { angle: 0.6, scale_x: 1.0, scale_y: 1.5, offset: 7 }, &bmp, x+144, y+48); - screen.blit_unchecked(BlitMethod::RotoZoomOffset { angle: 2.0, scale_x: 0.7, scale_y: 0.7, offset: 13 }, &bmp, x+208, y+48); - } + let x = 40; + let y = 110; + unsafe { + screen.blit_unchecked(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 0 }, &bmp, x + 16, y + 48); + screen.blit_unchecked(BlitMethod::RotoZoomOffset { angle: 0.3, scale_x: 1.5, scale_y: 1.0, offset: 4 }, &bmp, x + 80, y + 48); + screen.blit_unchecked(BlitMethod::RotoZoomOffset { angle: 0.6, scale_x: 1.0, scale_y: 1.5, offset: 7 }, &bmp, x + 144, y + 48); + screen.blit_unchecked(BlitMethod::RotoZoomOffset { angle: 2.0, scale_x: 0.7, scale_y: 0.7, offset: 13 }, &bmp, x + 208, y + 48); + } - ////// + ////// - screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 3 }, &bmp, -3, 46); - screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 3 }, &bmp, -4, 76); - screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 3 }, &bmp, -8, 106); - screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 3 }, &bmp, -12, 136); - screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 3 }, &bmp, -13, 166); - screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 3 }, &bmp, -14, 196); - screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 3 }, &bmp, -16, 226); + screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 3 }, &bmp, -3, 46); + screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 3 }, &bmp, -4, 76); + screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 3 }, &bmp, -8, 106); + screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 3 }, &bmp, -12, 136); + screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 3 }, &bmp, -13, 166); + screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 3 }, &bmp, -14, 196); + screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 3 }, &bmp, -16, 226); - screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 8 }, &bmp, 46, -3); - screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 8 }, &bmp, 76, -4); - screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 8 }, &bmp, 106, -8); - screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 8 }, &bmp, 136, -12); - screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 8 }, &bmp, 166, -13); - screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 8 }, &bmp, 196, -14); - screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 8 }, &bmp, 226, -16); + screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 8 }, &bmp, 46, -3); + screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 8 }, &bmp, 76, -4); + screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 8 }, &bmp, 106, -8); + screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 8 }, &bmp, 136, -12); + screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 8 }, &bmp, 166, -13); + screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 8 }, &bmp, 196, -14); + screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 8 }, &bmp, 226, -16); - screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 15 }, &bmp, 307, 46); - screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 15 }, &bmp, 308, 76); - screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 15 }, &bmp, 312, 106); - screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 15 }, &bmp, 316, 136); - screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 15 }, &bmp, 317, 166); - screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 15 }, &bmp, 318, 196); - screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 15 }, &bmp, 320, 226); + screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 15 }, &bmp, 307, 46); + screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 15 }, &bmp, 308, 76); + screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 15 }, &bmp, 312, 106); + screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 15 }, &bmp, 316, 136); + screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 15 }, &bmp, 317, 166); + screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 15 }, &bmp, 318, 196); + screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 15 }, &bmp, 320, 226); - screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 22 }, &bmp, 46, 227); - screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 22 }, &bmp, 76, 228); - screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 22 }, &bmp, 106, 232); - screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 22 }, &bmp, 136, 236); - screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 22 }, &bmp, 166, 237); - screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 22 }, &bmp, 196, 238); - screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 22 }, &bmp, 226, 240); + screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 22 }, &bmp, 46, 227); + screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 22 }, &bmp, 76, 228); + screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 22 }, &bmp, 106, 232); + screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 22 }, &bmp, 136, 236); + screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 22 }, &bmp, 166, 237); + screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 22 }, &bmp, 196, 238); + screen.blit(BlitMethod::RotoZoomOffset { angle: 1.3, scale_x: 1.0, scale_y: 1.0, offset: 22 }, &bmp, 226, 240); - let path = Path::new("tests/ref/rotozoom_offset_blits.pcx"); - //screen.to_pcx_file(path, &palette).unwrap(); - assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); + let path = Path::new("tests/ref/rotozoom_offset_blits.pcx"); + //screen.to_pcx_file(path, &palette).unwrap(); + assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); } #[test] fn rotozoom_transparent_blits() { - let (mut screen, palette) = setup(); - screen.clear(247); + let (mut screen, palette) = setup(); + screen.clear(247); - let bmp = generate_bitmap(16, 16); + let bmp = generate_bitmap(16, 16); - let x = 40; - let y = 20; - screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, x+16, y+48); - screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 0.3, scale_x: 1.5, scale_y: 1.0 }, &bmp, x+80, y+48); - screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 0.6, scale_x: 1.0, scale_y: 1.5 }, &bmp, x+144, y+48); - screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 2.0, scale_x: 0.7, scale_y: 0.7 }, &bmp, x+208, y+48); + let x = 40; + let y = 20; + screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, x + 16, y + 48); + screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 0.3, scale_x: 1.5, scale_y: 1.0 }, &bmp, x + 80, y + 48); + screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 0.6, scale_x: 1.0, scale_y: 1.5 }, &bmp, x + 144, y + 48); + screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 2.0, scale_x: 0.7, scale_y: 0.7 }, &bmp, x + 208, y + 48); - let x = 40; - let y = 110; - unsafe { - screen.blit_unchecked(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, x+16, y+48); - screen.blit_unchecked(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 0.3, scale_x: 1.5, scale_y: 1.0 }, &bmp, x+80, y+48); - screen.blit_unchecked(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 0.6, scale_x: 1.0, scale_y: 1.5 }, &bmp, x+144, y+48); - screen.blit_unchecked(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 2.0, scale_x: 0.7, scale_y: 0.7 }, &bmp, x+208, y+48); - } + let x = 40; + let y = 110; + unsafe { + screen.blit_unchecked(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, x + 16, y + 48); + screen.blit_unchecked(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 0.3, scale_x: 1.5, scale_y: 1.0 }, &bmp, x + 80, y + 48); + screen.blit_unchecked(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 0.6, scale_x: 1.0, scale_y: 1.5 }, &bmp, x + 144, y + 48); + screen.blit_unchecked(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 2.0, scale_x: 0.7, scale_y: 0.7 }, &bmp, x + 208, y + 48); + } - ////// + ////// - screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, -3, 46); - screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, -4, 76); - screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, -8, 106); - screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, -12, 136); - screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, -13, 166); - screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, -14, 196); - screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, -16, 226); + screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, -3, 46); + screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, -4, 76); + screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, -8, 106); + screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, -12, 136); + screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, -13, 166); + screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, -14, 196); + screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, -16, 226); - screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 46, -3); - screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 76, -4); - screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 106, -8); - screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 136, -12); - screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 166, -13); - screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 196, -14); - screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 226, -16); + screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 46, -3); + screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 76, -4); + screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 106, -8); + screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 136, -12); + screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 166, -13); + screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 196, -14); + screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 226, -16); - screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 307, 46); - screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 308, 76); - screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 312, 106); - screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 316, 136); - screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 317, 166); - screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 318, 196); - screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 320, 226); + screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 307, 46); + screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 308, 76); + screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 312, 106); + screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 316, 136); + screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 317, 166); + screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 318, 196); + screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 320, 226); - screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 46, 227); - screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 76, 228); - screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 106, 232); - screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 136, 236); - screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 166, 237); - screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 196, 238); - screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 226, 240); + screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 46, 227); + screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 76, 228); + screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 106, 232); + screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 136, 236); + screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 166, 237); + screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 196, 238); + screen.blit(BlitMethod::RotoZoomTransparent { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 226, 240); - let path = Path::new("tests/ref/rotozoom_transparent_blits.pcx"); - //screen.to_pcx_file(path, &palette).unwrap(); - assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); + let path = Path::new("tests/ref/rotozoom_transparent_blits.pcx"); + //screen.to_pcx_file(path, &palette).unwrap(); + assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); } #[test] fn blended_rotozoom_transparent_blits() { - let (mut screen, palette, blend_map) = setup_for_blending(); - let blend_map = Rc::new(blend_map); + let (mut screen, palette, blend_map) = setup_for_blending(); + let blend_map = Rc::new(blend_map); - let bmp = generate_bitmap(16, 16); + let bmp = generate_bitmap(16, 16); - let x = 40; - let y = 20; - screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, x+16, y+48); - screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 0.3, scale_x: 1.5, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, x+80, y+48); - screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 0.6, scale_x: 1.0, scale_y: 1.5, blend_map: blend_map.clone() }, &bmp, x+144, y+48); - screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 2.0, scale_x: 0.7, scale_y: 0.7, blend_map: blend_map.clone() }, &bmp, x+208, y+48); + let x = 40; + let y = 20; + screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, x + 16, y + 48); + screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 0.3, scale_x: 1.5, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, x + 80, y + 48); + screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 0.6, scale_x: 1.0, scale_y: 1.5, blend_map: blend_map.clone() }, &bmp, x + 144, y + 48); + screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 2.0, scale_x: 0.7, scale_y: 0.7, blend_map: blend_map.clone() }, &bmp, x + 208, y + 48); - let x = 40; - let y = 110; - unsafe { - screen.blit_unchecked(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, x+16, y+48); - screen.blit_unchecked(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 0.3, scale_x: 1.5, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, x+80, y+48); - screen.blit_unchecked(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 0.6, scale_x: 1.0, scale_y: 1.5, blend_map: blend_map.clone() }, &bmp, x+144, y+48); - screen.blit_unchecked(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 2.0, scale_x: 0.7, scale_y: 0.7, blend_map: blend_map.clone() }, &bmp, x+208, y+48); - } + let x = 40; + let y = 110; + unsafe { + screen.blit_unchecked(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, x + 16, y + 48); + screen.blit_unchecked(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 0.3, scale_x: 1.5, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, x + 80, y + 48); + screen.blit_unchecked(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 0.6, scale_x: 1.0, scale_y: 1.5, blend_map: blend_map.clone() }, &bmp, x + 144, y + 48); + screen.blit_unchecked(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 2.0, scale_x: 0.7, scale_y: 0.7, blend_map: blend_map.clone() }, &bmp, x + 208, y + 48); + } - ////// + ////// - screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, -3, 46); - screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, -4, 76); - screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, -8, 106); - screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, -12, 136); - screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, -13, 166); - screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, -14, 196); - screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, -16, 226); + screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, -3, 46); + screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, -4, 76); + screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, -8, 106); + screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, -12, 136); + screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, -13, 166); + screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, -14, 196); + screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, -16, 226); - screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 46, -3); - screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 76, -4); - screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 106, -8); - screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 136, -12); - screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 166, -13); - screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 196, -14); - screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 226, -16); + screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 46, -3); + screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 76, -4); + screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 106, -8); + screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 136, -12); + screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 166, -13); + screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 196, -14); + screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 226, -16); - screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 307, 46); - screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 308, 76); - screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 312, 106); - screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 316, 136); - screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 317, 166); - screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 318, 196); - screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 320, 226); + screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 307, 46); + screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 308, 76); + screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 312, 106); + screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 316, 136); + screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 317, 166); + screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 318, 196); + screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 320, 226); - screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 46, 227); - screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 76, 228); - screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 106, 232); - screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 136, 236); - screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 166, 237); - screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 196, 238); - screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 226, 240); + screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 46, 227); + screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 76, 228); + screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 106, 232); + screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 136, 236); + screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 166, 237); + screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 196, 238); + screen.blit(BlitMethod::RotoZoomTransparentBlended { transparent_color: 0, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() }, &bmp, 226, 240); - let path = Path::new("tests/ref/blended_rotozoom_transparent_blits.pcx"); - //screen.to_pcx_file(path, &palette).unwrap(); - assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); + let path = Path::new("tests/ref/blended_rotozoom_transparent_blits.pcx"); + //screen.to_pcx_file(path, &palette).unwrap(); + assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); } #[test] fn rotozoom_transparent_offset_blits() { - let (mut screen, palette) = setup(); - screen.clear(247); + let (mut screen, palette) = setup(); + screen.clear(247); - let bmp = generate_bitmap(16, 16); + let bmp = generate_bitmap(16, 16); - let x = 40; - let y = 20; - screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 1, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, x+16, y+48); - screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 4, angle: 0.3, scale_x: 1.5, scale_y: 1.0 }, &bmp, x+80, y+48); - screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 7, angle: 0.6, scale_x: 1.0, scale_y: 1.5 }, &bmp, x+144, y+48); - screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 13, angle: 2.0, scale_x: 0.7, scale_y: 0.7 }, &bmp, x+208, y+48); + let x = 40; + let y = 20; + screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 1, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, x + 16, y + 48); + screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 4, angle: 0.3, scale_x: 1.5, scale_y: 1.0 }, &bmp, x + 80, y + 48); + screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 7, angle: 0.6, scale_x: 1.0, scale_y: 1.5 }, &bmp, x + 144, y + 48); + screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 13, angle: 2.0, scale_x: 0.7, scale_y: 0.7 }, &bmp, x + 208, y + 48); - let x = 40; - let y = 110; - unsafe { - screen.blit_unchecked(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 1, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, x+16, y+48); - screen.blit_unchecked(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 4, angle: 0.3, scale_x: 1.5, scale_y: 1.0 }, &bmp, x+80, y+48); - screen.blit_unchecked(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 7, angle: 0.6, scale_x: 1.0, scale_y: 1.5 }, &bmp, x+144, y+48); - screen.blit_unchecked(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 13, angle: 2.0, scale_x: 0.7, scale_y: 0.7 }, &bmp, x+208, y+48); - } + let x = 40; + let y = 110; + unsafe { + screen.blit_unchecked(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 1, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, x + 16, y + 48); + screen.blit_unchecked(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 4, angle: 0.3, scale_x: 1.5, scale_y: 1.0 }, &bmp, x + 80, y + 48); + screen.blit_unchecked(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 7, angle: 0.6, scale_x: 1.0, scale_y: 1.5 }, &bmp, x + 144, y + 48); + screen.blit_unchecked(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 13, angle: 2.0, scale_x: 0.7, scale_y: 0.7 }, &bmp, x + 208, y + 48); + } - ////// + ////// - screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 3, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, -3, 46); - screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 3, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, -4, 76); - screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 3, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, -8, 106); - screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 3, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, -12, 136); - screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 3, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, -13, 166); - screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 3, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, -14, 196); - screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 3, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, -16, 226); + screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 3, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, -3, 46); + screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 3, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, -4, 76); + screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 3, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, -8, 106); + screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 3, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, -12, 136); + screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 3, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, -13, 166); + screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 3, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, -14, 196); + screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 3, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, -16, 226); - screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 8, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 46, -3); - screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 8, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 76, -4); - screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 8, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 106, -8); - screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 8, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 136, -12); - screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 8, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 166, -13); - screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 8, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 196, -14); - screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 8, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 226, -16); + screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 8, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 46, -3); + screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 8, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 76, -4); + screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 8, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 106, -8); + screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 8, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 136, -12); + screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 8, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 166, -13); + screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 8, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 196, -14); + screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 8, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 226, -16); - screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 15, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 307, 46); - screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 15, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 308, 76); - screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 15, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 312, 106); - screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 15, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 316, 136); - screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 15, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 317, 166); - screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 15, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 318, 196); - screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 15, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 320, 226); + screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 15, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 307, 46); + screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 15, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 308, 76); + screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 15, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 312, 106); + screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 15, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 316, 136); + screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 15, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 317, 166); + screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 15, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 318, 196); + screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 15, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 320, 226); - screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 22, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 46, 227); - screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 22, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 76, 228); - screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 22, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 106, 232); - screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 22, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 136, 236); - screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 22, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 166, 237); - screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 22, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 196, 238); - screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 22, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 226, 240); + screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 22, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 46, 227); + screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 22, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 76, 228); + screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 22, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 106, 232); + screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 22, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 136, 236); + screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 22, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 166, 237); + screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 22, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 196, 238); + screen.blit(BlitMethod::RotoZoomTransparentOffset { transparent_color: 0, offset: 22, angle: 1.3, scale_x: 1.0, scale_y: 1.0 }, &bmp, 226, 240); - let path = Path::new("tests/ref/rotozoom_transparent_offset_blits.pcx"); - //screen.to_pcx_file(path, &palette).unwrap(); - assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); + let path = Path::new("tests/ref/rotozoom_transparent_offset_blits.pcx"); + //screen.to_pcx_file(path, &palette).unwrap(); + assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); }