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); }