lets try using rustfmt again ...
i unfortunately feel like i should really force myself to use rustfmt even though i very much dislike it. most rust developers seem to use it so i should probably get used to it ... however, case-in-point for me is the amount of times i used either #[rustfmt::skip] or adding a blank `//` comment to force separate lines, etc, really proves how terrible basing almost all of your formatting rules on arbitrary length thresholds really is. code formatting is far more subjective than that.
This commit is contained in:
parent
36c4cc1a41
commit
ceaefad030
|
@ -30,15 +30,13 @@ pub struct SineWaveGenerator {
|
||||||
|
|
||||||
impl SineWaveGenerator {
|
impl SineWaveGenerator {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
SineWaveGenerator {
|
SineWaveGenerator { t: 0 }
|
||||||
t: 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AudioGenerator for SineWaveGenerator {
|
impl AudioGenerator for SineWaveGenerator {
|
||||||
fn gen_sample(&mut self, _position: usize) -> Option<u8> {
|
fn gen_sample(&mut self, _position: usize) -> Option<u8> {
|
||||||
const MAX_TIME: usize = AUDIO_FREQUENCY_22KHZ as usize * 3; // 3 seconds
|
const MAX_TIME: usize = AUDIO_FREQUENCY_22KHZ as usize * 3; // 3 seconds
|
||||||
if self.t < MAX_TIME {
|
if self.t < MAX_TIME {
|
||||||
let sample = (self.t as f64 * 0.25).sin() * 80.0;
|
let sample = (self.t as f64 * 0.25).sin() * 80.0;
|
||||||
self.t += 1;
|
self.t += 1;
|
||||||
|
@ -51,7 +49,7 @@ impl AudioGenerator for SineWaveGenerator {
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let config = DosLikeConfig::new();
|
let config = DosLikeConfig::new();
|
||||||
let mut system = SystemBuilder::new()
|
let mut system = SystemBuilder::new() //
|
||||||
.window_title("Audio Playback")
|
.window_title("Audio Playback")
|
||||||
.vsync(true)
|
.vsync(true)
|
||||||
.build(config)?;
|
.build(config)?;
|
||||||
|
@ -169,21 +167,20 @@ fn main() -> Result<()> {
|
||||||
|
|
||||||
system.res.video.print_string(
|
system.res.video.print_string(
|
||||||
&format!("Volume: {:2.2}", volume),
|
&format!("Volume: {:2.2}", volume),
|
||||||
16, 16, FontRenderOpts::Color(10), &system.res.font
|
16,
|
||||||
|
16,
|
||||||
|
FontRenderOpts::Color(10),
|
||||||
|
&system.res.font,
|
||||||
);
|
);
|
||||||
system.res.video.print_string(
|
system.res.video.print_string(
|
||||||
if using_queue_commands {
|
if using_queue_commands { "Queueing Commands" } else { "Direct Commands" },
|
||||||
"Queueing Commands"
|
160,
|
||||||
} else {
|
16,
|
||||||
"Direct Commands"
|
FontRenderOpts::Color(9),
|
||||||
},
|
&system.res.font,
|
||||||
160, 16, FontRenderOpts::Color(9), &system.res.font,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
system.res.video.print_string(
|
system.res.video.print_string("Audio Channels", 16, 32, FontRenderOpts::Color(14), &system.res.font);
|
||||||
"Audio Channels",
|
|
||||||
16, 32, FontRenderOpts::Color(14), &system.res.font
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut y = 48;
|
let mut y = 48;
|
||||||
for index in 0..NUM_CHANNELS {
|
for index in 0..NUM_CHANNELS {
|
||||||
|
@ -193,9 +190,14 @@ fn main() -> Result<()> {
|
||||||
"channel {} - {} {}",
|
"channel {} - {} {}",
|
||||||
index,
|
index,
|
||||||
if status.playing { "playing" } else { "not playing" },
|
if status.playing { "playing" } else { "not playing" },
|
||||||
if status.playing { String::from(format!("{} / {}", status.position, status.size)) } else { String::new() }
|
if status.playing {
|
||||||
|
String::from(format!("{} / {}", status.position, status.size))
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
}
|
||||||
),
|
),
|
||||||
16, y,
|
16,
|
||||||
|
y,
|
||||||
FontRenderOpts::Color(15),
|
FontRenderOpts::Color(15),
|
||||||
&system.res.font,
|
&system.res.font,
|
||||||
);
|
);
|
||||||
|
|
|
@ -19,7 +19,7 @@ struct Ball {
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let config = DosLikeConfig::new();
|
let config = DosLikeConfig::new();
|
||||||
let mut system = SystemBuilder::new()
|
let mut system = SystemBuilder::new() //
|
||||||
.window_title("Flying Balls!")
|
.window_title("Flying Balls!")
|
||||||
.vsync(true)
|
.vsync(true)
|
||||||
.build(config)?;
|
.build(config)?;
|
||||||
|
@ -99,12 +99,7 @@ fn main() -> Result<()> {
|
||||||
system.res.video.print_string("hello, world!", 10, 10, FontRenderOpts::Color(15), &font);
|
system.res.video.print_string("hello, world!", 10, 10, FontRenderOpts::Color(15), &font);
|
||||||
|
|
||||||
for i in 0..NUM_BALLS {
|
for i in 0..NUM_BALLS {
|
||||||
system.res.video.blit(
|
system.res.video.blit(IndexedBlitMethod::Transparent(0), &sprites[balls[i].sprite], balls[i].x, balls[i].y);
|
||||||
IndexedBlitMethod::Transparent(0),
|
|
||||||
&sprites[balls[i].sprite],
|
|
||||||
balls[i].x,
|
|
||||||
balls[i].y,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
system.display()?;
|
system.display()?;
|
||||||
|
|
|
@ -38,7 +38,15 @@ pub enum Event {
|
||||||
LeaveTrail(Vector2),
|
LeaveTrail(Vector2),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_basic_particle_entity(entities: &mut Entities, x: f32, y: f32, color: u8, lifetime: f32, angle: f32, speed: i32) {
|
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();
|
let id = entities.new_entity();
|
||||||
entities.add_component(id, Particle);
|
entities.add_component(id, Particle);
|
||||||
entities.add_component(id, Color(color));
|
entities.add_component(id, Color(color));
|
||||||
|
|
|
@ -12,7 +12,7 @@ mod states;
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let config = DosLikeConfig::new();
|
let config = DosLikeConfig::new();
|
||||||
let system = SystemBuilder::new()
|
let system = SystemBuilder::new() //
|
||||||
.window_title("Flying Balls")
|
.window_title("Flying Balls")
|
||||||
.vsync(true)
|
.vsync(true)
|
||||||
.build(config)?;
|
.build(config)?;
|
||||||
|
|
|
@ -50,14 +50,7 @@ impl Game {
|
||||||
init_event_listeners(&mut event_listeners);
|
init_event_listeners(&mut event_listeners);
|
||||||
|
|
||||||
Ok(Game {
|
Ok(Game {
|
||||||
context: Context {
|
context: Context { delta: 0.0, system, font, sprites, entities, event_publisher },
|
||||||
delta: 0.0,
|
|
||||||
system,
|
|
||||||
font,
|
|
||||||
sprites,
|
|
||||||
entities,
|
|
||||||
event_publisher,
|
|
||||||
},
|
|
||||||
component_systems,
|
component_systems,
|
||||||
event_listeners,
|
event_listeners,
|
||||||
})
|
})
|
||||||
|
@ -88,7 +81,13 @@ impl AppState<Game> for SimulationState {
|
||||||
fn render(&mut self, _state: State, context: &mut Game) {
|
fn render(&mut self, _state: State, context: &mut Game) {
|
||||||
context.context.system.res.video.clear(2);
|
context.context.system.res.video.clear(2);
|
||||||
context.component_systems.render(&mut context.context);
|
context.component_systems.render(&mut context.context);
|
||||||
context.context.system.res.video.print_string("hello, world!", 10, 10, FontRenderOpts::Color(15), &context.context.font);
|
context.context.system.res.video.print_string(
|
||||||
|
"hello, world!",
|
||||||
|
10,
|
||||||
|
10,
|
||||||
|
FontRenderOpts::Color(15),
|
||||||
|
&context.context.font,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transition(&mut self, _state: State, _context: &mut Game) -> bool {
|
fn transition(&mut self, _state: State, _context: &mut Game) -> bool {
|
||||||
|
@ -100,4 +99,4 @@ impl AppState<Game> for SimulationState {
|
||||||
init_entities(&mut context.context.entities);
|
init_entities(&mut context.context.entities);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use ggdt::prelude::*;
|
use ggdt::prelude::*;
|
||||||
|
|
||||||
use crate::Core;
|
|
||||||
use crate::entities::*;
|
use crate::entities::*;
|
||||||
|
use crate::Core;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
|
@ -89,4 +89,4 @@ fn event_handler(event: &Event, context: &mut Core) -> bool {
|
||||||
pub fn init_events(event_listener: &mut EventListeners<Event, Core>) {
|
pub fn init_events(event_listener: &mut EventListeners<Event, Core>) {
|
||||||
event_listener.clear();
|
event_listener.clear();
|
||||||
event_listener.add(event_handler);
|
event_listener.add(event_handler);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ use std::rc::Rc;
|
||||||
|
|
||||||
use ggdt::prelude::*;
|
use ggdt::prelude::*;
|
||||||
|
|
||||||
use crate::{Core, Game, TILE_HEIGHT, TILE_WIDTH, TileMap};
|
use crate::{Core, Game, TileMap, TILE_HEIGHT, TILE_WIDTH};
|
||||||
|
|
||||||
pub use self::events::*;
|
pub use self::events::*;
|
||||||
pub use self::systems::*;
|
pub use self::systems::*;
|
||||||
|
@ -46,7 +46,7 @@ impl Direction {
|
||||||
1 => West,
|
1 => West,
|
||||||
2 => East,
|
2 => East,
|
||||||
3 => North,
|
3 => North,
|
||||||
_ => panic!("unknown random direction!")
|
_ => panic!("unknown random direction!"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,7 @@ impl SlimeColor {
|
||||||
0 => Green,
|
0 => Green,
|
||||||
1 => Blue,
|
1 => Blue,
|
||||||
2 => Orange,
|
2 => Orange,
|
||||||
_ => panic!("unknown random slime color!")
|
_ => panic!("unknown random slime color!"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ impl PickupType {
|
||||||
1 => BlueGem,
|
1 => BlueGem,
|
||||||
2 => OrangeGem,
|
2 => OrangeGem,
|
||||||
3 => Coin,
|
3 => Coin,
|
||||||
_ => panic!("unknown random pickup type!")
|
_ => panic!("unknown random pickup type!"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,12 +116,7 @@ pub struct AnimationDef {
|
||||||
impl AnimationDef {
|
impl AnimationDef {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(frames: &'static [usize], loops: bool, delay: f32, multi_direction_offset: Option<usize>) -> Self {
|
pub fn new(frames: &'static [usize], loops: bool, delay: f32, multi_direction_offset: Option<usize>) -> Self {
|
||||||
AnimationDef {
|
AnimationDef { frames, loops, delay, multi_direction_offset }
|
||||||
frames,
|
|
||||||
loops,
|
|
||||||
delay,
|
|
||||||
multi_direction_offset,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,7 +133,7 @@ impl AnimationInstance {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from(def: Rc<AnimationDef>) -> Self {
|
pub fn from(def: Rc<AnimationDef>) -> Self {
|
||||||
AnimationInstance {
|
AnimationInstance {
|
||||||
def,
|
def, //
|
||||||
frame_index: 0,
|
frame_index: 0,
|
||||||
frame_timer: 0.0,
|
frame_timer: 0.0,
|
||||||
complete: false,
|
complete: false,
|
||||||
|
@ -174,9 +169,7 @@ pub struct Forces {
|
||||||
|
|
||||||
impl Forces {
|
impl Forces {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Forces {
|
Forces { forces: Vec::with_capacity(5) }
|
||||||
forces: Vec::with_capacity(5),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn current_force(&self) -> Vector2 {
|
pub fn current_force(&self) -> Vector2 {
|
||||||
|
@ -232,7 +225,13 @@ pub struct RandomlyWalksAround {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RandomlyWalksAround {
|
impl RandomlyWalksAround {
|
||||||
pub fn new(min_walk_time: f32, max_walk_time: f32, chance_to_move: u32, min_cooldown: f32, max_cooldown: f32) -> Self {
|
pub fn new(
|
||||||
|
min_walk_time: f32,
|
||||||
|
max_walk_time: f32,
|
||||||
|
chance_to_move: u32,
|
||||||
|
min_cooldown: f32,
|
||||||
|
max_cooldown: f32,
|
||||||
|
) -> Self {
|
||||||
RandomlyWalksAround {
|
RandomlyWalksAround {
|
||||||
min_walk_time,
|
min_walk_time,
|
||||||
max_walk_time,
|
max_walk_time,
|
||||||
|
@ -264,7 +263,7 @@ pub struct SpawnTimer {
|
||||||
impl SpawnTimer {
|
impl SpawnTimer {
|
||||||
pub fn new(min_time: f32, max_time: f32, max_allowed: usize) -> Self {
|
pub fn new(min_time: f32, max_time: f32, max_allowed: usize) -> Self {
|
||||||
SpawnTimer {
|
SpawnTimer {
|
||||||
timer: 0.0,
|
timer: 0.0, //
|
||||||
min_time,
|
min_time,
|
||||||
max_time,
|
max_time,
|
||||||
max_allowed,
|
max_allowed,
|
||||||
|
@ -291,7 +290,7 @@ pub struct Pusher {
|
||||||
impl Pusher {
|
impl Pusher {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Pusher {
|
Pusher {
|
||||||
strength: DEFAULT_PUSH_STRENGTH,
|
strength: DEFAULT_PUSH_STRENGTH, //
|
||||||
push_force_dissipation: DEFAULT_PUSH_DISSIPATION,
|
push_force_dissipation: DEFAULT_PUSH_DISSIPATION,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -338,7 +337,7 @@ pub struct TimedFlicker {
|
||||||
impl TimedFlicker {
|
impl TimedFlicker {
|
||||||
pub fn new(timer: f32, method: FlickerMethod) -> Self {
|
pub fn new(timer: f32, method: FlickerMethod) -> Self {
|
||||||
TimedFlicker {
|
TimedFlicker {
|
||||||
timer,
|
timer, //
|
||||||
method,
|
method,
|
||||||
pre_timer: None,
|
pre_timer: None,
|
||||||
flick: true,
|
flick: true,
|
||||||
|
@ -347,7 +346,7 @@ impl TimedFlicker {
|
||||||
|
|
||||||
pub fn new_with_pre_timer(timer: f32, pre_timer: f32, method: FlickerMethod) -> Self {
|
pub fn new_with_pre_timer(timer: f32, pre_timer: f32, method: FlickerMethod) -> Self {
|
||||||
TimedFlicker {
|
TimedFlicker {
|
||||||
timer,
|
timer, //
|
||||||
method,
|
method,
|
||||||
pre_timer: Some(pre_timer),
|
pre_timer: Some(pre_timer),
|
||||||
flick: true,
|
flick: true,
|
||||||
|
@ -380,7 +379,13 @@ pub struct Pickuper;
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
pub fn init_everything(context: &mut Game, map_file: &Path, min_spawn_time: f32, max_spawn_time: f32, max_slimes: usize) {
|
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_entities(&mut context.core.entities);
|
||||||
init_component_system(&mut context.support.component_systems);
|
init_component_system(&mut context.support.component_systems);
|
||||||
init_events(&mut context.support.event_listeners);
|
init_events(&mut context.support.event_listeners);
|
||||||
|
@ -456,7 +461,17 @@ pub fn new_camera_entity(context: &mut Core, x: i32, y: i32) -> EntityId {
|
||||||
pub fn new_slime_entity(context: &mut Core, x: i32, y: i32, direction: Direction, color: SlimeColor) -> EntityId {
|
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 {
|
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::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::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),
|
SlimeColor::Orange => (context.orange_slime.clone(), 90, 24.0, 0.5, 1.0, 0.5, 2.0, 3, 9),
|
||||||
|
@ -476,7 +491,10 @@ pub fn new_slime_entity(context: &mut Core, x: i32, y: i32, direction: Direction
|
||||||
context.entities.add_component(id, Activity(activity));
|
context.entities.add_component(id, Activity(activity));
|
||||||
context.entities.add_component(id, animate_by_activity);
|
context.entities.add_component(id, animate_by_activity);
|
||||||
context.entities.add_component(id, animation);
|
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,
|
||||||
|
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, MovementSpeed(movement_speed));
|
||||||
context.entities.add_component(id, Pusher::new());
|
context.entities.add_component(id, Pusher::new());
|
||||||
context.entities.add_component(id, Pushable);
|
context.entities.add_component(id, Pushable);
|
||||||
|
@ -489,7 +507,13 @@ pub fn new_slime_entity(context: &mut Core, x: i32, y: i32, direction: Direction
|
||||||
|
|
||||||
pub fn spawn_slime_randomly(context: &mut Core) -> EntityId {
|
pub fn spawn_slime_randomly(context: &mut Core) -> EntityId {
|
||||||
let (x, y) = context.tilemap.get_random_spawnable_coordinates();
|
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());
|
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);
|
spawn_poof_cloud(context, x * TILE_WIDTH as i32, y * TILE_HEIGHT as i32, 4, 8);
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
@ -501,21 +525,21 @@ pub fn new_player_entity(context: &mut Core, x: i32, y: i32, direction: Directio
|
||||||
(
|
(
|
||||||
context.hero_female.clone(),
|
context.hero_female.clone(),
|
||||||
[
|
[
|
||||||
Vector2::new(-3.0, 13.0),
|
Vector2::new(-3.0, 13.0), //
|
||||||
Vector2::new(-14.0, 2.0),
|
Vector2::new(-14.0, 2.0),
|
||||||
Vector2::new(14.0, 2.0),
|
Vector2::new(14.0, 2.0),
|
||||||
Vector2::new(3.0, -11.0)
|
Vector2::new(3.0, -11.0),
|
||||||
]
|
],
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
(
|
(
|
||||||
context.hero_male.clone(),
|
context.hero_male.clone(),
|
||||||
[
|
[
|
||||||
Vector2::new(-3.0, 13.0),
|
Vector2::new(-3.0, 13.0), //
|
||||||
Vector2::new(-13.0, 2.0),
|
Vector2::new(-13.0, 2.0),
|
||||||
Vector2::new(13.0, 2.0),
|
Vector2::new(13.0, 2.0),
|
||||||
Vector2::new(3.0, -11.0)
|
Vector2::new(3.0, -11.0),
|
||||||
]
|
],
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -524,7 +548,7 @@ pub fn new_player_entity(context: &mut Core, x: i32, y: i32, direction: Directio
|
||||||
let animation = AnimationInstance::from(animate_by_activity.0.get(&activity).unwrap().clone());
|
let animation = AnimationInstance::from(animate_by_activity.0.get(&activity).unwrap().clone());
|
||||||
|
|
||||||
let weapon = Weapon {
|
let weapon = Weapon {
|
||||||
atlas: context.sword.clone(),
|
atlas: context.sword.clone(), //
|
||||||
base_index: 0,
|
base_index: 0,
|
||||||
offsets: weapon_offsets,
|
offsets: weapon_offsets,
|
||||||
damage: 1,
|
damage: 1,
|
||||||
|
@ -555,7 +579,13 @@ pub fn spawn_player_randomly(context: &mut Core) -> EntityId {
|
||||||
new_player_entity(context, x * TILE_WIDTH as i32, y * TILE_HEIGHT as i32, Direction::South)
|
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<AnimationDef>, delay_scaling_factor: Option<f32>) -> EntityId {
|
fn new_animation_effect(
|
||||||
|
context: &mut Core,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
animation_def: Rc<AnimationDef>,
|
||||||
|
delay_scaling_factor: Option<f32>,
|
||||||
|
) -> EntityId {
|
||||||
let id = context.entities.new_entity();
|
let id = context.entities.new_entity();
|
||||||
context.entities.add_component(id, Particle);
|
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, Position(Vector2::new(x as f32, y as f32)));
|
||||||
|
@ -573,11 +603,17 @@ fn new_animation_effect(context: &mut Core, x: i32, y: i32, animation_def: Rc<An
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_poof_animation(context: &mut Core, x: i32, y: i32, variant: usize, delay_scaling_factor: Option<f32>) -> EntityId {
|
pub fn new_poof_animation(
|
||||||
|
context: &mut Core,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
variant: usize,
|
||||||
|
delay_scaling_factor: Option<f32>,
|
||||||
|
) -> EntityId {
|
||||||
let def = match variant {
|
let def = match variant {
|
||||||
0 => context.poof1_animation_def.clone(),
|
0 => context.poof1_animation_def.clone(),
|
||||||
1 => context.poof2_animation_def.clone(),
|
1 => context.poof2_animation_def.clone(),
|
||||||
_ => panic!("unknown poof animation variant")
|
_ => panic!("unknown poof animation variant"),
|
||||||
};
|
};
|
||||||
new_animation_effect(context, x, y, def, delay_scaling_factor)
|
new_animation_effect(context, x, y, def, delay_scaling_factor)
|
||||||
}
|
}
|
||||||
|
@ -611,15 +647,21 @@ pub fn spawn_poof_cloud(context: &mut Core, x: i32, y: i32, count: usize, radius
|
||||||
for _ in 0..count {
|
for _ in 0..count {
|
||||||
let x = x + rnd_value(-radius, radius);
|
let x = x + rnd_value(-radius, radius);
|
||||||
let y = y + rnd_value(-radius, radius);
|
let y = y + rnd_value(-radius, radius);
|
||||||
new_poof_animation(context, x, y, 0, match rnd_value(0, 5) {
|
new_poof_animation(
|
||||||
0 => Some(0.25),
|
context,
|
||||||
1 => Some(0.5),
|
x,
|
||||||
2 => Some(0.75),
|
y,
|
||||||
3 => Some(1.0),
|
0,
|
||||||
4 => Some(1.25),
|
match rnd_value(0, 5) {
|
||||||
5 => Some(1.5),
|
0 => Some(0.25),
|
||||||
_ => None,
|
1 => Some(0.5),
|
||||||
});
|
2 => Some(0.75),
|
||||||
|
3 => Some(1.0),
|
||||||
|
4 => Some(1.25),
|
||||||
|
5 => Some(1.5),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -691,7 +733,7 @@ pub fn spawn_pickups_from_entity(context: &mut Core, entity: EntityId) {
|
||||||
let angle = (rnd_value(0, 359) as f32).to_radians();
|
let angle = (rnd_value(0, 359) as f32).to_radians();
|
||||||
let force_strength = rnd_value(0.5, 5.0);
|
let force_strength = rnd_value(0.5, 5.0);
|
||||||
let force = Force {
|
let force = Force {
|
||||||
force: Vector2::from_angle(angle) * force_strength,
|
force: Vector2::from_angle(angle) * force_strength, //
|
||||||
dissipation_factor: 0.5,
|
dissipation_factor: 0.5,
|
||||||
};
|
};
|
||||||
let kind = PickupType::new_random();
|
let kind = PickupType::new_random();
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use ggdt::prelude::*;
|
use ggdt::prelude::*;
|
||||||
|
|
||||||
use crate::{Core, TILE_HEIGHT, TILE_WIDTH};
|
|
||||||
use crate::entities::*;
|
use crate::entities::*;
|
||||||
use crate::tilemap::*;
|
use crate::tilemap::*;
|
||||||
|
use crate::{Core, TILE_HEIGHT, TILE_WIDTH};
|
||||||
|
|
||||||
pub fn remove_entity(entities: &mut Entities, entity: EntityId) {
|
pub fn remove_entity(entities: &mut Entities, entity: EntityId) {
|
||||||
remove_entity_attachment(entities, entity);
|
remove_entity_attachment(entities, entity);
|
||||||
|
@ -31,7 +31,7 @@ pub fn move_entity_forward(context: &mut Core, entity: EntityId) {
|
||||||
Direction::North => Vector2::UP * movement_speed.0,
|
Direction::North => Vector2::UP * movement_speed.0,
|
||||||
Direction::East => Vector2::RIGHT * movement_speed.0,
|
Direction::East => Vector2::RIGHT * movement_speed.0,
|
||||||
Direction::West => Vector2::LEFT * movement_speed.0,
|
Direction::West => Vector2::LEFT * movement_speed.0,
|
||||||
Direction::South => Vector2::DOWN * movement_speed.0
|
Direction::South => Vector2::DOWN * movement_speed.0,
|
||||||
};
|
};
|
||||||
|
|
||||||
velocity.0 += movement;
|
velocity.0 += movement;
|
||||||
|
@ -56,7 +56,14 @@ pub fn turn_and_move_entity(context: &mut Core, entity: EntityId, direction: Dir
|
||||||
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 {
|
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 NUM_STEPS: usize = 2;
|
||||||
const STEP_SCALE: f32 = 1.0 / NUM_STEPS as f32;
|
const STEP_SCALE: f32 = 1.0 / NUM_STEPS as f32;
|
||||||
|
|
||||||
|
@ -169,12 +176,8 @@ pub fn get_attack_area_of_effect(context: &mut Core, attacker: EntityId) -> Opti
|
||||||
if let Some(facing_direction) = facing_directions.get(&attacker) {
|
if let Some(facing_direction) = facing_directions.get(&attacker) {
|
||||||
let center_point = position.0 + weapon.offsets[facing_direction.0 as usize];
|
let center_point = position.0 + weapon.offsets[facing_direction.0 as usize];
|
||||||
return Some((
|
return Some((
|
||||||
Circle::new(
|
Circle::new(center_point.x as i32 + 8, center_point.y as i32 + 8, weapon.radius_of_effect),
|
||||||
center_point.x as i32 + 8,
|
weapon.damage,
|
||||||
center_point.y as i32 + 8,
|
|
||||||
weapon.radius_of_effect,
|
|
||||||
),
|
|
||||||
weapon.damage
|
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
return Some((
|
return Some((
|
||||||
|
@ -183,7 +186,7 @@ pub fn get_attack_area_of_effect(context: &mut Core, attacker: EntityId) -> Opti
|
||||||
position.0.y as i32 + bound.height as i32 / 2,
|
position.0.y as i32 + bound.height as i32 / 2,
|
||||||
weapon.radius_of_effect,
|
weapon.radius_of_effect,
|
||||||
),
|
),
|
||||||
weapon.damage
|
weapon.damage,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -337,7 +340,11 @@ fn update_system_pushing(context: &mut Core) {
|
||||||
|
|
||||||
let pushable_position = positions.get(pushable_entity).unwrap();
|
let pushable_position = positions.get(pushable_entity).unwrap();
|
||||||
let pushable_bounds = bounds.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_circle = Circle::new(
|
||||||
|
pushable_position.0.x as i32, //
|
||||||
|
pushable_position.0.y as i32,
|
||||||
|
pushable_bounds.radius,
|
||||||
|
);
|
||||||
|
|
||||||
if pusher_circle.overlaps(&pushable_circle) {
|
if pusher_circle.overlaps(&pushable_circle) {
|
||||||
let push_direction = (pushable_position.0 - pusher_position.0).normalize();
|
let push_direction = (pushable_position.0 - pusher_position.0).normalize();
|
||||||
|
@ -371,7 +378,7 @@ fn update_system_animation(context: &mut Core) {
|
||||||
animation.frame_timer += context.delta;
|
animation.frame_timer += context.delta;
|
||||||
|
|
||||||
let delay = if let Some(delay_override) = animation.delay_override {
|
let delay = if let Some(delay_override) = animation.delay_override {
|
||||||
delay_override
|
delay_override //
|
||||||
} else {
|
} else {
|
||||||
animation.def.delay
|
animation.def.delay
|
||||||
};
|
};
|
||||||
|
@ -469,7 +476,10 @@ fn update_system_randomly_walk_around(context: &mut Core) {
|
||||||
randomly_walk_around.cooldown_timer = 0.0;
|
randomly_walk_around.cooldown_timer = 0.0;
|
||||||
}
|
}
|
||||||
} else if randomly_walk_around.should_start_walking() {
|
} 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);
|
randomly_walk_around.cooldown_timer = rnd_value(
|
||||||
|
randomly_walk_around.min_cooldown, //
|
||||||
|
randomly_walk_around.max_cooldown,
|
||||||
|
);
|
||||||
|
|
||||||
let direction = Direction::new_random();
|
let direction = Direction::new_random();
|
||||||
let walk_time = rnd_value(randomly_walk_around.min_walk_time, randomly_walk_around.max_walk_time);
|
let walk_time = rnd_value(randomly_walk_around.min_walk_time, randomly_walk_around.max_walk_time);
|
||||||
|
@ -674,7 +684,7 @@ fn render_system_sprites(context: &mut Core) {
|
||||||
}
|
}
|
||||||
FlickerMethod::Color(draw_color) => {
|
FlickerMethod::Color(draw_color) => {
|
||||||
blit_method = IndexedBlitMethod::TransparentSingle {
|
blit_method = IndexedBlitMethod::TransparentSingle {
|
||||||
transparent_color: 0,
|
transparent_color: 0, //
|
||||||
draw_color,
|
draw_color,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,8 +13,8 @@ use crate::states::*;
|
||||||
use crate::support::*;
|
use crate::support::*;
|
||||||
use crate::tilemap::*;
|
use crate::tilemap::*;
|
||||||
|
|
||||||
mod states;
|
|
||||||
mod entities;
|
mod entities;
|
||||||
|
mod states;
|
||||||
mod support;
|
mod support;
|
||||||
mod tilemap;
|
mod tilemap;
|
||||||
|
|
||||||
|
@ -182,17 +182,14 @@ impl Game {
|
||||||
sparkles_animation_def,
|
sparkles_animation_def,
|
||||||
sprite_render_list: Vec::with_capacity(1024),
|
sprite_render_list: Vec::with_capacity(1024),
|
||||||
},
|
},
|
||||||
support: Support {
|
support: Support { component_systems, event_listeners },
|
||||||
component_systems,
|
|
||||||
event_listeners,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let config = DosLikeConfig::new();
|
let config = DosLikeConfig::new();
|
||||||
let system = SystemBuilder::new()
|
let system = SystemBuilder::new() //
|
||||||
.window_title("Slime Stabbing Simulator")
|
.window_title("Slime Stabbing Simulator")
|
||||||
.vsync(true)
|
.vsync(true)
|
||||||
.build(config)?;
|
.build(config)?;
|
||||||
|
|
|
@ -3,8 +3,8 @@ use std::path::Path;
|
||||||
use ggdt::prelude::*;
|
use ggdt::prelude::*;
|
||||||
|
|
||||||
use crate::entities::*;
|
use crate::entities::*;
|
||||||
use crate::Game;
|
|
||||||
use crate::support::*;
|
use crate::support::*;
|
||||||
|
use crate::Game;
|
||||||
|
|
||||||
pub struct MainMenuState {
|
pub struct MainMenuState {
|
||||||
fade: f32,
|
fade: f32,
|
||||||
|
@ -13,10 +13,7 @@ pub struct MainMenuState {
|
||||||
|
|
||||||
impl MainMenuState {
|
impl MainMenuState {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
MainMenuState {
|
MainMenuState { fade: 0.0, selection: 0 }
|
||||||
fade: 0.0,
|
|
||||||
selection: 0,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,10 +57,28 @@ impl AppState<Game> for MainMenuState {
|
||||||
draw_window(&mut context.core.system.res.video, &context.core.ui, x, y, x + width, y + height);
|
draw_window(&mut context.core.system.res.video, &context.core.ui, x, y, x + width, y + height);
|
||||||
|
|
||||||
let selection_y = y + SPACER + (self.selection as i32 * 16);
|
let selection_y = y + SPACER + (self.selection as i32 * 16);
|
||||||
context.core.system.res.video.print_string(">", x + SPACER, selection_y, FontRenderOpts::Color(15), &context.core.font);
|
context.core.system.res.video.print_string(
|
||||||
|
">",
|
||||||
|
x + SPACER,
|
||||||
|
selection_y,
|
||||||
|
FontRenderOpts::Color(15),
|
||||||
|
&context.core.font,
|
||||||
|
);
|
||||||
|
|
||||||
context.core.system.res.video.print_string("Play", x + SPACER + SPACER, y + SPACER, FontRenderOpts::Color(15), &context.core.font);
|
context.core.system.res.video.print_string(
|
||||||
context.core.system.res.video.print_string("Quit", x + SPACER + SPACER, y + SPACER + 16, FontRenderOpts::Color(15), &context.core.font);
|
"Play",
|
||||||
|
x + SPACER + SPACER,
|
||||||
|
y + SPACER,
|
||||||
|
FontRenderOpts::Color(15),
|
||||||
|
&context.core.font,
|
||||||
|
);
|
||||||
|
context.core.system.res.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 {
|
fn transition(&mut self, state: State, context: &mut Game) -> bool {
|
||||||
|
@ -98,7 +113,7 @@ pub struct GamePlayState {
|
||||||
impl GamePlayState {
|
impl GamePlayState {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
GamePlayState {
|
GamePlayState {
|
||||||
fade: 0.0,
|
fade: 0.0, //
|
||||||
in_menu: false,
|
in_menu: false,
|
||||||
selection: 0,
|
selection: 0,
|
||||||
}
|
}
|
||||||
|
@ -172,10 +187,28 @@ impl AppState<Game> for GamePlayState {
|
||||||
draw_window(&mut context.core.system.res.video, &context.core.ui, x, y, x + width, y + height);
|
draw_window(&mut context.core.system.res.video, &context.core.ui, x, y, x + width, y + height);
|
||||||
|
|
||||||
let selection_y = y + SPACER + (self.selection as i32 * 16);
|
let selection_y = y + SPACER + (self.selection as i32 * 16);
|
||||||
context.core.system.res.video.print_string(">", x + SPACER, selection_y, FontRenderOpts::Color(15), &context.core.font);
|
context.core.system.res.video.print_string(
|
||||||
|
">",
|
||||||
|
x + SPACER,
|
||||||
|
selection_y,
|
||||||
|
FontRenderOpts::Color(15),
|
||||||
|
&context.core.font,
|
||||||
|
);
|
||||||
|
|
||||||
context.core.system.res.video.print_string("Continue", x + SPACER + SPACER, y + SPACER, FontRenderOpts::Color(15), &context.core.font);
|
context.core.system.res.video.print_string(
|
||||||
context.core.system.res.video.print_string("Quit", x + SPACER + SPACER, y + SPACER + 16, FontRenderOpts::Color(15), &context.core.font);
|
"Continue",
|
||||||
|
x + SPACER + SPACER,
|
||||||
|
y + SPACER,
|
||||||
|
FontRenderOpts::Color(15),
|
||||||
|
&context.core.font,
|
||||||
|
);
|
||||||
|
context.core.system.res.video.print_string(
|
||||||
|
"Quit",
|
||||||
|
x + SPACER + SPACER,
|
||||||
|
y + SPACER + 16,
|
||||||
|
FontRenderOpts::Color(15),
|
||||||
|
&context.core.font,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,14 @@ pub fn load_bitmap_atlas(path: &Path) -> Result<BitmapAtlas<IndexedBitmap>> {
|
||||||
Ok(atlas)
|
Ok(atlas)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_window(dest: &mut IndexedBitmap, ui: &BitmapAtlas<IndexedBitmap>, left: i32, top: i32, right: i32, bottom: i32) {
|
pub fn draw_window(
|
||||||
|
dest: &mut IndexedBitmap,
|
||||||
|
ui: &BitmapAtlas<IndexedBitmap>,
|
||||||
|
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
|
// corners
|
||||||
|
@ -60,7 +67,12 @@ pub fn update_fade_transition(state: State, fade: &mut f32, delta: f32, context:
|
||||||
context.core.system.res.palette = context.core.palette.clone();
|
context.core.system.res.palette = context.core.palette.clone();
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
context.core.system.res.palette.lerp(0..=255, &context.core.fade_out_palette, &context.core.palette, *fade);
|
context.core.system.res.palette.lerp(
|
||||||
|
0..=255,
|
||||||
|
&context.core.fade_out_palette,
|
||||||
|
&context.core.palette,
|
||||||
|
*fade,
|
||||||
|
);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,12 +83,15 @@ pub fn update_fade_transition(state: State, fade: &mut f32, delta: f32, context:
|
||||||
context.core.system.res.palette = context.core.fade_out_palette.clone();
|
context.core.system.res.palette = context.core.fade_out_palette.clone();
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
context.core.system.res.palette.lerp(0..=255, &context.core.fade_out_palette, &context.core.palette, *fade);
|
context.core.system.res.palette.lerp(
|
||||||
|
0..=255,
|
||||||
|
&context.core.fade_out_palette,
|
||||||
|
&context.core.palette,
|
||||||
|
*fade,
|
||||||
|
);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => true,
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,7 +79,13 @@ impl TileMap {
|
||||||
}
|
}
|
||||||
let upper = self.layers[1][index];
|
let upper = self.layers[1][index];
|
||||||
if upper >= 0 {
|
if upper >= 0 {
|
||||||
dest.blit_region(IndexedBlitMethod::Transparent(0), tiles.bitmap(), &tiles[upper as usize], xd, yd);
|
dest.blit_region(
|
||||||
|
IndexedBlitMethod::Transparent(0),
|
||||||
|
tiles.bitmap(),
|
||||||
|
&tiles[upper as usize],
|
||||||
|
xd,
|
||||||
|
yd,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -100,7 +106,7 @@ impl TileMap {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => return true
|
None => return true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ pub fn event_listener(event: &Event, context: &mut Core) -> bool {
|
||||||
context.entities.add_component(id, Color(color));
|
context.entities.add_component(id, Color(color));
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
_ => false
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,16 +201,8 @@ impl App {
|
||||||
let event_listeners = EventListeners::new();
|
let event_listeners = EventListeners::new();
|
||||||
|
|
||||||
Ok(App {
|
Ok(App {
|
||||||
core: Core {
|
core: Core { delta: 0.0, system, entities, event_publisher },
|
||||||
delta: 0.0,
|
support: Support { component_systems, event_listeners },
|
||||||
system,
|
|
||||||
entities,
|
|
||||||
event_publisher,
|
|
||||||
},
|
|
||||||
support: Support {
|
|
||||||
component_systems,
|
|
||||||
event_listeners,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -219,7 +211,7 @@ impl App {
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let config = DosLikeConfig::new();
|
let config = DosLikeConfig::new();
|
||||||
let system = SystemBuilder::new()
|
let system = SystemBuilder::new() //
|
||||||
.window_title("Complicated Template")
|
.window_title("Complicated Template")
|
||||||
.vsync(true)
|
.vsync(true)
|
||||||
.build(config)?;
|
.build(config)?;
|
||||||
|
|
|
@ -4,7 +4,7 @@ use ggdt::prelude::*;
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let config = DosLikeConfig::new();
|
let config = DosLikeConfig::new();
|
||||||
let mut system = SystemBuilder::new()
|
let mut system = SystemBuilder::new() //
|
||||||
.window_title("Minimal Template")
|
.window_title("Minimal Template")
|
||||||
.vsync(true)
|
.vsync(true)
|
||||||
.build(config)?;
|
.build(config)?;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use criterion::{black_box, Criterion, criterion_group, criterion_main};
|
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||||
|
|
||||||
use ggdt::prelude::*;
|
use ggdt::prelude::*;
|
||||||
|
|
||||||
|
@ -10,18 +10,12 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
let mut dest = vec![0u32; (width * height * 4) as usize].into_boxed_slice();
|
let mut dest = vec![0u32; (width * height * 4) as usize].into_boxed_slice();
|
||||||
let palette = Palette::new_vga_palette().unwrap();
|
let palette = Palette::new_vga_palette().unwrap();
|
||||||
|
|
||||||
c.bench_function("deindex_bitmap_pixels", |b| {
|
c.bench_function("deindex_bitmap_pixels", |b| b.iter(|| source.copy_as_argb_to(&mut dest, &palette)));
|
||||||
b.iter(|| source.copy_as_argb_to(&mut dest, &palette))
|
|
||||||
});
|
|
||||||
|
|
||||||
c.bench_function("set_pixel", |b| {
|
c.bench_function("set_pixel", |b| b.iter(|| source.set_pixel(black_box(100), black_box(100), black_box(42))));
|
||||||
b.iter(|| source.set_pixel(black_box(100), black_box(100), black_box(42)))
|
|
||||||
});
|
|
||||||
|
|
||||||
c.bench_function("set_pixel_unchecked", |b| {
|
c.bench_function("set_pixel_unchecked", |b| {
|
||||||
b.iter(|| unsafe {
|
b.iter(|| unsafe { source.set_pixel_unchecked(black_box(100), black_box(100), black_box(42)) })
|
||||||
source.set_pixel_unchecked(black_box(100), black_box(100), black_box(42))
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use criterion::{black_box, Criterion, criterion_group, criterion_main};
|
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||||
|
|
||||||
use ggdt::prelude::*;
|
use ggdt::prelude::*;
|
||||||
|
|
||||||
|
@ -17,12 +17,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
|
|
||||||
c.bench_function("blit_single_checked_solid", |b| {
|
c.bench_function("blit_single_checked_solid", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
framebuffer.blit(
|
framebuffer.blit(black_box(IndexedBlitMethod::Solid), black_box(&solid_bmp), black_box(100), black_box(100))
|
||||||
black_box(IndexedBlitMethod::Solid),
|
|
||||||
black_box(&solid_bmp),
|
|
||||||
black_box(100),
|
|
||||||
black_box(100),
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -66,10 +61,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
c.bench_function("blit_solid_flipped_not_flipped", |b| {
|
c.bench_function("blit_solid_flipped_not_flipped", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
framebuffer.blit(
|
framebuffer.blit(
|
||||||
black_box(IndexedBlitMethod::SolidFlipped {
|
black_box(IndexedBlitMethod::SolidFlipped { horizontal_flip: false, vertical_flip: false }),
|
||||||
horizontal_flip: false,
|
|
||||||
vertical_flip: false,
|
|
||||||
}),
|
|
||||||
black_box(&solid_bmp),
|
black_box(&solid_bmp),
|
||||||
black_box(100),
|
black_box(100),
|
||||||
black_box(100),
|
black_box(100),
|
||||||
|
@ -80,10 +72,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
c.bench_function("blit_solid_flipped_horizontally", |b| {
|
c.bench_function("blit_solid_flipped_horizontally", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
framebuffer.blit(
|
framebuffer.blit(
|
||||||
black_box(IndexedBlitMethod::SolidFlipped {
|
black_box(IndexedBlitMethod::SolidFlipped { horizontal_flip: true, vertical_flip: false }),
|
||||||
horizontal_flip: true,
|
|
||||||
vertical_flip: false,
|
|
||||||
}),
|
|
||||||
black_box(&solid_bmp),
|
black_box(&solid_bmp),
|
||||||
black_box(100),
|
black_box(100),
|
||||||
black_box(100),
|
black_box(100),
|
||||||
|
@ -94,10 +83,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
c.bench_function("blit_solid_flipped_vertically", |b| {
|
c.bench_function("blit_solid_flipped_vertically", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
framebuffer.blit(
|
framebuffer.blit(
|
||||||
black_box(IndexedBlitMethod::SolidFlipped {
|
black_box(IndexedBlitMethod::SolidFlipped { horizontal_flip: false, vertical_flip: true }),
|
||||||
horizontal_flip: false,
|
|
||||||
vertical_flip: true,
|
|
||||||
}),
|
|
||||||
black_box(&solid_bmp),
|
black_box(&solid_bmp),
|
||||||
black_box(100),
|
black_box(100),
|
||||||
black_box(100),
|
black_box(100),
|
||||||
|
@ -108,10 +94,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
c.bench_function("blit_solid_flipped_horizontally_and_vertically", |b| {
|
c.bench_function("blit_solid_flipped_horizontally_and_vertically", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
framebuffer.blit(
|
framebuffer.blit(
|
||||||
black_box(IndexedBlitMethod::SolidFlipped {
|
black_box(IndexedBlitMethod::SolidFlipped { horizontal_flip: true, vertical_flip: true }),
|
||||||
horizontal_flip: true,
|
|
||||||
vertical_flip: true,
|
|
||||||
}),
|
|
||||||
black_box(&solid_bmp),
|
black_box(&solid_bmp),
|
||||||
black_box(100),
|
black_box(100),
|
||||||
black_box(100),
|
black_box(100),
|
||||||
|
@ -252,10 +235,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
c.bench_function("blit_transparent_single", |b| {
|
c.bench_function("blit_transparent_single", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
framebuffer.blit(
|
framebuffer.blit(
|
||||||
black_box(IndexedBlitMethod::TransparentSingle {
|
black_box(IndexedBlitMethod::TransparentSingle { transparent_color: 0, draw_color: 17 }),
|
||||||
transparent_color: 0,
|
|
||||||
draw_color: 17,
|
|
||||||
}),
|
|
||||||
black_box(&trans_bmp),
|
black_box(&trans_bmp),
|
||||||
black_box(100),
|
black_box(100),
|
||||||
black_box(100),
|
black_box(100),
|
||||||
|
@ -268,10 +248,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
c.bench_function("blit_transparent_offset", |b| {
|
c.bench_function("blit_transparent_offset", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
framebuffer.blit(
|
framebuffer.blit(
|
||||||
black_box(IndexedBlitMethod::TransparentOffset {
|
black_box(IndexedBlitMethod::TransparentOffset { transparent_color: 0, offset: 42 }),
|
||||||
transparent_color: 0,
|
|
||||||
offset: 42,
|
|
||||||
}),
|
|
||||||
black_box(&trans_bmp),
|
black_box(&trans_bmp),
|
||||||
black_box(100),
|
black_box(100),
|
||||||
black_box(100),
|
black_box(100),
|
||||||
|
@ -425,11 +402,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
c.bench_function("blit_rotozoom", |b| {
|
c.bench_function("blit_rotozoom", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
framebuffer.blit(
|
framebuffer.blit(
|
||||||
black_box(IndexedBlitMethod::RotoZoom {
|
black_box(IndexedBlitMethod::RotoZoom { angle: 73.0f32.to_radians(), scale_x: 1.2, scale_y: 0.8 }),
|
||||||
angle: 73.0f32.to_radians(),
|
|
||||||
scale_x: 1.2,
|
|
||||||
scale_y: 0.8,
|
|
||||||
}),
|
|
||||||
black_box(&solid_bmp),
|
black_box(&solid_bmp),
|
||||||
black_box(100),
|
black_box(100),
|
||||||
black_box(100),
|
black_box(100),
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
|
|
||||||
use criterion::{black_box, Criterion, criterion_group, criterion_main};
|
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||||
|
|
||||||
use ggdt::prelude::*;
|
use ggdt::prelude::*;
|
||||||
|
|
||||||
|
|
|
@ -30,10 +30,7 @@ impl AudioBuffer {
|
||||||
/// Creates and returns a new, empty, [`AudioBuffer`] that will hold audio sample data in the
|
/// Creates and returns a new, empty, [`AudioBuffer`] that will hold audio sample data in the
|
||||||
/// spec/format given.
|
/// spec/format given.
|
||||||
pub fn new(spec: AudioSpec) -> Self {
|
pub fn new(spec: AudioSpec) -> Self {
|
||||||
AudioBuffer {
|
AudioBuffer { spec, data: Vec::new() }
|
||||||
spec,
|
|
||||||
data: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the spec of the audio sample data that this buffer contains.
|
/// Returns the spec of the audio sample data that this buffer contains.
|
||||||
|
|
|
@ -7,8 +7,8 @@ use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||||
use sdl2::audio::AudioFormat;
|
use sdl2::audio::AudioFormat;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::audio::AudioSpec;
|
|
||||||
use crate::audio::buffer::AudioBuffer;
|
use crate::audio::buffer::AudioBuffer;
|
||||||
|
use crate::audio::AudioSpec;
|
||||||
use crate::utils::io::StreamSize;
|
use crate::utils::io::StreamSize;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
|
@ -70,10 +70,7 @@ impl WavHeader {
|
||||||
pub fn read<T: ReadBytesExt>(reader: &mut T) -> Result<Self, WavError> {
|
pub fn read<T: ReadBytesExt>(reader: &mut T) -> Result<Self, WavError> {
|
||||||
let file_chunk = SubChunkHeader::read(reader)?;
|
let file_chunk = SubChunkHeader::read(reader)?;
|
||||||
let file_container_id = ChunkId::read(reader)?;
|
let file_container_id = ChunkId::read(reader)?;
|
||||||
Ok(WavHeader {
|
Ok(WavHeader { file_chunk, file_container_id })
|
||||||
file_chunk,
|
|
||||||
file_container_id,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
@ -98,10 +95,7 @@ struct FormatChunk {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FormatChunk {
|
impl FormatChunk {
|
||||||
pub fn read<T: ReadBytesExt>(
|
pub fn read<T: ReadBytesExt>(reader: &mut T, chunk_header: &SubChunkHeader) -> Result<Self, WavError> {
|
||||||
reader: &mut T,
|
|
||||||
chunk_header: &SubChunkHeader,
|
|
||||||
) -> Result<Self, WavError> {
|
|
||||||
let compression_code = reader.read_u16::<LittleEndian>()?;
|
let compression_code = reader.read_u16::<LittleEndian>()?;
|
||||||
let channels = reader.read_u16::<LittleEndian>()?;
|
let channels = reader.read_u16::<LittleEndian>()?;
|
||||||
let frequency = reader.read_u32::<LittleEndian>()?;
|
let frequency = reader.read_u32::<LittleEndian>()?;
|
||||||
|
@ -180,9 +174,7 @@ impl DataChunk {
|
||||||
buffer = vec![0u8; chunk_header.size as usize];
|
buffer = vec![0u8; chunk_header.size as usize];
|
||||||
reader.read_exact(&mut buffer)?;
|
reader.read_exact(&mut buffer)?;
|
||||||
}
|
}
|
||||||
Ok(DataChunk {
|
Ok(DataChunk { data: buffer.into_boxed_slice() })
|
||||||
data: buffer.into_boxed_slice(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
@ -200,14 +192,10 @@ impl AudioBuffer {
|
||||||
|
|
||||||
let header = WavHeader::read(reader)?;
|
let header = WavHeader::read(reader)?;
|
||||||
if header.file_chunk.chunk_id.id != *b"RIFF" {
|
if header.file_chunk.chunk_id.id != *b"RIFF" {
|
||||||
return Err(WavError::BadFile(String::from(
|
return Err(WavError::BadFile(String::from("Unexpected RIFF chunk id, probably not a WAV file")));
|
||||||
"Unexpected RIFF chunk id, probably not a WAV file",
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
if header.file_container_id.id != *b"WAVE" {
|
if header.file_container_id.id != *b"WAVE" {
|
||||||
return Err(WavError::BadFile(String::from(
|
return Err(WavError::BadFile(String::from("Unexpected RIFF container id, probably not a WAV file")));
|
||||||
"Unexpected RIFF container id, probably not a WAV file",
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// some tools like sfxr and jsfxr incorrectly calculate data sizes, seemingly using a
|
// some tools like sfxr and jsfxr incorrectly calculate data sizes, seemingly using a
|
||||||
|
@ -227,11 +215,9 @@ impl AudioBuffer {
|
||||||
loop {
|
loop {
|
||||||
let chunk_header = match SubChunkHeader::read(reader) {
|
let chunk_header = match SubChunkHeader::read(reader) {
|
||||||
Ok(header) => header,
|
Ok(header) => header,
|
||||||
Err(WavError::IOError(io_error))
|
Err(WavError::IOError(io_error)) if io_error.kind() == io::ErrorKind::UnexpectedEof => {
|
||||||
if io_error.kind() == io::ErrorKind::UnexpectedEof =>
|
break;
|
||||||
{
|
}
|
||||||
break;
|
|
||||||
}
|
|
||||||
Err(err) => return Err(err),
|
Err(err) => return Err(err),
|
||||||
};
|
};
|
||||||
let chunk_data_position = reader.stream_position()?;
|
let chunk_data_position = reader.stream_position()?;
|
||||||
|
@ -240,12 +226,9 @@ impl AudioBuffer {
|
||||||
if chunk_header.chunk_id.id == *b"fmt " {
|
if chunk_header.chunk_id.id == *b"fmt " {
|
||||||
format = Some(FormatChunk::read(reader, &chunk_header)?);
|
format = Some(FormatChunk::read(reader, &chunk_header)?);
|
||||||
if format.as_ref().unwrap().compression_code != 1 {
|
if format.as_ref().unwrap().compression_code != 1 {
|
||||||
return Err(WavError::BadFile(String::from(
|
return Err(WavError::BadFile(String::from("Only PCM format WAV files are supported")));
|
||||||
"Only PCM format WAV files are supported",
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
if format.as_ref().unwrap().bits_per_sample != 8 &&
|
if format.as_ref().unwrap().bits_per_sample != 8 && format.as_ref().unwrap().bits_per_sample != 16 {
|
||||||
format.as_ref().unwrap().bits_per_sample != 16 {
|
|
||||||
return Err(WavError::BadFile(String::from(
|
return Err(WavError::BadFile(String::from(
|
||||||
"Only 8-bit and 16-bit sample WAV files are supported",
|
"Only 8-bit and 16-bit sample WAV files are supported",
|
||||||
)));
|
)));
|
||||||
|
@ -256,9 +239,7 @@ impl AudioBuffer {
|
||||||
|
|
||||||
// move to the start of the next chunk (possibly skipping over the current chunk if we
|
// move to the start of the next chunk (possibly skipping over the current chunk if we
|
||||||
// didn't recognize it above ...)
|
// didn't recognize it above ...)
|
||||||
reader.seek(SeekFrom::Start(
|
reader.seek(SeekFrom::Start(chunk_data_position + chunk_header.size as u64))?;
|
||||||
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 ...
|
||||||
|
@ -271,7 +252,7 @@ impl AudioBuffer {
|
||||||
16 => AudioFormat::S16LSB,
|
16 => AudioFormat::S16LSB,
|
||||||
// this shouldn't be able to happen given the above checks when reading the
|
// this shouldn't be able to happen given the above checks when reading the
|
||||||
// "fmt" chunk
|
// "fmt" chunk
|
||||||
_ => return Err(WavError::BadFile(String::from("Unsupported sample bit size.")))
|
_ => return Err(WavError::BadFile(String::from("Unsupported sample bit size."))),
|
||||||
};
|
};
|
||||||
let spec = AudioSpec::new(format.frequency, format.channels as u8, sample_format);
|
let spec = AudioSpec::new(format.frequency, format.channels as u8, sample_format);
|
||||||
audio_buffer = AudioBuffer::new(spec);
|
audio_buffer = AudioBuffer::new(spec);
|
||||||
|
|
|
@ -3,8 +3,8 @@ use std::ops::{Index, IndexMut};
|
||||||
use sdl2::audio::AudioCallback;
|
use sdl2::audio::AudioCallback;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::audio::{AudioGenerator, AudioSpec, NUM_CHANNELS};
|
|
||||||
use crate::audio::buffer::AudioBuffer;
|
use crate::audio::buffer::AudioBuffer;
|
||||||
|
use crate::audio::{AudioGenerator, AudioSpec, NUM_CHANNELS};
|
||||||
|
|
||||||
/// Represents a "channel" of audio playback that will be mixed together with all of the other
|
/// 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.
|
/// actively playing audio channels to get the final audio playback.
|
||||||
|
@ -36,10 +36,13 @@ impl std::fmt::Debug for AudioChannel {
|
||||||
.field("playing", &self.playing)
|
.field("playing", &self.playing)
|
||||||
.field("loops", &self.loops)
|
.field("loops", &self.loops)
|
||||||
.field("data.len()", &self.data.len())
|
.field("data.len()", &self.data.len())
|
||||||
.field("generator", match self.generator {
|
.field(
|
||||||
Some(..) => &"Some(..)",
|
"generator",
|
||||||
None => &"None",
|
match self.generator {
|
||||||
})
|
Some(..) => &"Some(..)",
|
||||||
|
None => &"None",
|
||||||
|
},
|
||||||
|
)
|
||||||
.field("volume", &self.volume)
|
.field("volume", &self.volume)
|
||||||
.field("position", &self.position)
|
.field("position", &self.position)
|
||||||
.finish_non_exhaustive()
|
.finish_non_exhaustive()
|
||||||
|
@ -49,7 +52,7 @@ impl std::fmt::Debug for AudioChannel {
|
||||||
impl AudioChannel {
|
impl AudioChannel {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
AudioChannel {
|
AudioChannel {
|
||||||
playing: false,
|
playing: false, //
|
||||||
loops: false,
|
loops: false,
|
||||||
volume: 1.0,
|
volume: 1.0,
|
||||||
position: 0,
|
position: 0,
|
||||||
|
@ -221,11 +224,7 @@ impl AudioDevice {
|
||||||
for _ in 0..NUM_CHANNELS {
|
for _ in 0..NUM_CHANNELS {
|
||||||
channels.push(AudioChannel::new());
|
channels.push(AudioChannel::new());
|
||||||
}
|
}
|
||||||
AudioDevice {
|
AudioDevice { spec, channels, volume: 1.0 }
|
||||||
spec,
|
|
||||||
channels,
|
|
||||||
volume: 1.0,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the spec that this device is currently set to play. All audio to be played via
|
/// Returns the spec that this device is currently set to play. All audio to be played via
|
||||||
|
@ -263,11 +262,7 @@ impl AudioDevice {
|
||||||
/// playing. If a free channel is found, playback will be started by copying the buffer's
|
/// 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
|
/// 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.
|
/// because no channel is free currently, then `None` is returned.
|
||||||
pub fn play_buffer(
|
pub fn play_buffer(&mut self, buffer: &AudioBuffer, loops: bool) -> Result<Option<usize>, AudioDeviceError> {
|
||||||
&mut self,
|
|
||||||
buffer: &AudioBuffer,
|
|
||||||
loops: bool,
|
|
||||||
) -> Result<Option<usize>, AudioDeviceError> {
|
|
||||||
if *buffer.spec() != self.spec {
|
if *buffer.spec() != self.spec {
|
||||||
Err(AudioDeviceError::AudioSpecMismatch)
|
Err(AudioDeviceError::AudioSpecMismatch)
|
||||||
} else {
|
} else {
|
||||||
|
@ -333,37 +328,37 @@ impl AudioDevice {
|
||||||
|
|
||||||
/// Returns an iterator of any [`AudioChannel`]s that are currently playing.
|
/// Returns an iterator of any [`AudioChannel`]s that are currently playing.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn playing_channels_iter(&mut self) -> impl Iterator<Item=&AudioChannel> {
|
pub fn playing_channels_iter(&mut self) -> impl Iterator<Item = &AudioChannel> {
|
||||||
self.channels.iter().filter(|channel| channel.playing)
|
self.channels.iter().filter(|channel| channel.playing)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an iterator of mutable [`AudioChannel`]s that are currently playing.
|
/// Returns an iterator of mutable [`AudioChannel`]s that are currently playing.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn playing_channels_iter_mut(&mut self) -> impl Iterator<Item=&mut AudioChannel> {
|
pub fn playing_channels_iter_mut(&mut self) -> impl Iterator<Item = &mut AudioChannel> {
|
||||||
self.channels.iter_mut().filter(|channel| channel.playing)
|
self.channels.iter_mut().filter(|channel| channel.playing)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an iterator of [`AudioChannel`]s that are not currently playing.
|
/// Returns an iterator of [`AudioChannel`]s that are not currently playing.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn stopped_channels_iter(&mut self) -> impl Iterator<Item=&AudioChannel> {
|
pub fn stopped_channels_iter(&mut self) -> impl Iterator<Item = &AudioChannel> {
|
||||||
self.channels.iter().filter(|channel| !channel.playing)
|
self.channels.iter().filter(|channel| !channel.playing)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an iterator of mutable [`AudioChannel`]s that are not currently playing.
|
/// Returns an iterator of mutable [`AudioChannel`]s that are not currently playing.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn stopped_channels_iter_mut(&mut self) -> impl Iterator<Item=&mut AudioChannel> {
|
pub fn stopped_channels_iter_mut(&mut self) -> impl Iterator<Item = &mut AudioChannel> {
|
||||||
self.channels.iter_mut().filter(|channel| !channel.playing)
|
self.channels.iter_mut().filter(|channel| !channel.playing)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an iterator of all [`AudioChannel`]s.
|
/// Returns an iterator of all [`AudioChannel`]s.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn channels_iter(&mut self) -> impl Iterator<Item=&AudioChannel> {
|
pub fn channels_iter(&mut self) -> impl Iterator<Item = &AudioChannel> {
|
||||||
self.channels.iter()
|
self.channels.iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an iterator of all [`AudioChannel`]s as mutable references.
|
/// Returns an iterator of all [`AudioChannel`]s as mutable references.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn channels_iter_mut(&mut self) -> impl Iterator<Item=&mut AudioChannel> {
|
pub fn channels_iter_mut(&mut self) -> impl Iterator<Item = &mut AudioChannel> {
|
||||||
self.channels.iter_mut()
|
self.channels.iter_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,11 +44,7 @@ impl AudioSpec {
|
||||||
/// * `channels`: the number of channels of the audio (e.g. 1 = mono, 2 = stereo, etc)
|
/// * `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.
|
/// * `format`: indicates the format of the bytes making up the audio buffer.
|
||||||
pub fn new(frequency: u32, channels: u8, format: AudioFormat) -> Self {
|
pub fn new(frequency: u32, channels: u8, format: AudioFormat) -> Self {
|
||||||
AudioSpec {
|
AudioSpec { frequency, channels, format }
|
||||||
frequency,
|
|
||||||
channels,
|
|
||||||
format,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -98,8 +94,8 @@ pub struct Audio {
|
||||||
|
|
||||||
impl std::fmt::Debug for Audio {
|
impl std::fmt::Debug for Audio {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
f.debug_struct("Audio")
|
f.debug_struct("Audio") //
|
||||||
.field("spec", &self.spec)
|
.field("spec", &self.spec) //
|
||||||
.finish_non_exhaustive()
|
.finish_non_exhaustive()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,34 +106,25 @@ impl Audio {
|
||||||
///
|
///
|
||||||
/// Ideally, you should not be creating an instance of this yourself and should just use the
|
/// Ideally, you should not be creating an instance of this yourself and should just use the
|
||||||
/// one provided by [`crate::system::System`].
|
/// one provided by [`crate::system::System`].
|
||||||
pub fn new(
|
pub fn new(desired_spec: AudioSpecDesired, sdl_audio_subsystem: &AudioSubsystem) -> Result<Self, AudioError> {
|
||||||
desired_spec: AudioSpecDesired,
|
|
||||||
sdl_audio_subsystem: &AudioSubsystem,
|
|
||||||
) -> Result<Self, AudioError> {
|
|
||||||
let mut spec = None;
|
let mut spec = None;
|
||||||
let sdl_audio_device =
|
let sdl_audio_device = match sdl_audio_subsystem.open_playback(None, &desired_spec, |opened_spec| {
|
||||||
match sdl_audio_subsystem.open_playback(None, &desired_spec, |opened_spec| {
|
let our_spec = AudioSpec::new(
|
||||||
let our_spec = AudioSpec::new(
|
opened_spec.freq as u32, //
|
||||||
opened_spec.freq as u32,
|
opened_spec.channels, //
|
||||||
opened_spec.channels,
|
opened_spec.format, //
|
||||||
opened_spec.format,
|
);
|
||||||
);
|
spec = Some(our_spec);
|
||||||
spec = Some(our_spec);
|
AudioDevice::new(our_spec)
|
||||||
AudioDevice::new(our_spec)
|
}) {
|
||||||
}) {
|
Ok(audio_device) => audio_device,
|
||||||
Ok(audio_device) => audio_device,
|
Err(error) => return Err(AudioError::OpenDeviceFailed(error)),
|
||||||
Err(error) => return Err(AudioError::OpenDeviceFailed(error)),
|
};
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(spec) = spec {
|
if let Some(spec) = spec {
|
||||||
Ok(Audio {
|
Ok(Audio { spec, sdl_audio_device })
|
||||||
spec,
|
|
||||||
sdl_audio_device,
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
Err(AudioError::OpenDeviceFailed(String::from(
|
Err(AudioError::OpenDeviceFailed(String::from("Device initialization failed to set AudioSpec")))
|
||||||
"Device initialization failed to set AudioSpec",
|
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
audio::*,
|
//
|
||||||
audio::buffer::*,
|
|
||||||
audio::buffer::wav::*,
|
audio::buffer::wav::*,
|
||||||
|
audio::buffer::*,
|
||||||
audio::device::*,
|
audio::device::*,
|
||||||
audio::queue::*,
|
audio::queue::*,
|
||||||
};
|
audio::*,
|
||||||
|
};
|
||||||
|
|
|
@ -1,37 +1,37 @@
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::audio::{Audio, AudioGenerator, AudioSpec, NUM_CHANNELS};
|
|
||||||
use crate::audio::buffer::AudioBuffer;
|
use crate::audio::buffer::AudioBuffer;
|
||||||
use crate::audio::device::{AudioDevice, AudioDeviceError};
|
use crate::audio::device::{AudioDevice, AudioDeviceError};
|
||||||
|
use crate::audio::{Audio, AudioGenerator, AudioSpec, NUM_CHANNELS};
|
||||||
|
|
||||||
pub enum AudioCommand {
|
pub enum AudioCommand {
|
||||||
StopChannel(usize),
|
StopChannel(usize),
|
||||||
StopAllChannels,
|
StopAllChannels,
|
||||||
PlayBuffer {
|
PlayBuffer {
|
||||||
buffer: AudioBuffer,
|
buffer: AudioBuffer, //
|
||||||
loops: bool,
|
loops: bool,
|
||||||
},
|
},
|
||||||
PlayRcBuffer {
|
PlayRcBuffer {
|
||||||
buffer: Rc<AudioBuffer>,
|
buffer: Rc<AudioBuffer>, //
|
||||||
loops: bool,
|
loops: bool,
|
||||||
},
|
},
|
||||||
PlayBufferOnChannel {
|
PlayBufferOnChannel {
|
||||||
channel: usize,
|
channel: usize, //
|
||||||
buffer: AudioBuffer,
|
buffer: AudioBuffer,
|
||||||
loops: bool,
|
loops: bool,
|
||||||
},
|
},
|
||||||
PlayRcBufferOnChannel {
|
PlayRcBufferOnChannel {
|
||||||
channel: usize,
|
channel: usize, //
|
||||||
buffer: Rc<AudioBuffer>,
|
buffer: Rc<AudioBuffer>,
|
||||||
loops: bool,
|
loops: bool,
|
||||||
},
|
},
|
||||||
PlayGenerator {
|
PlayGenerator {
|
||||||
generator: Box<dyn AudioGenerator>,
|
generator: Box<dyn AudioGenerator>, //
|
||||||
loops: bool,
|
loops: bool,
|
||||||
},
|
},
|
||||||
PlayGeneratorOnChannel {
|
PlayGeneratorOnChannel {
|
||||||
channel: usize,
|
channel: usize, //
|
||||||
generator: Box<dyn AudioGenerator>,
|
generator: Box<dyn AudioGenerator>,
|
||||||
loops: bool,
|
loops: bool,
|
||||||
},
|
},
|
||||||
|
@ -44,38 +44,38 @@ impl std::fmt::Debug for AudioCommand {
|
||||||
StopChannel(n) => write!(f, "StopChannel({})", n),
|
StopChannel(n) => write!(f, "StopChannel({})", n),
|
||||||
StopAllChannels => write!(f, "StopAllChannels"),
|
StopAllChannels => write!(f, "StopAllChannels"),
|
||||||
PlayBuffer { buffer, loops } => {
|
PlayBuffer { buffer, loops } => {
|
||||||
f.debug_struct("PlayBuffer")
|
f.debug_struct("PlayBuffer") //
|
||||||
.field("buffer", buffer)
|
.field("buffer", buffer)
|
||||||
.field("loops", loops)
|
.field("loops", loops)
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
PlayRcBuffer { buffer, loops } => {
|
PlayRcBuffer { buffer, loops } => {
|
||||||
f.debug_struct("PlayRcBuffer")
|
f.debug_struct("PlayRcBuffer") //
|
||||||
.field("buffer", buffer)
|
.field("buffer", buffer)
|
||||||
.field("loops", loops)
|
.field("loops", loops)
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
PlayBufferOnChannel { channel, buffer, loops } => {
|
PlayBufferOnChannel { channel, buffer, loops } => {
|
||||||
f.debug_struct("PlayBufferOnChannel")
|
f.debug_struct("PlayBufferOnChannel") //
|
||||||
.field("channel", channel)
|
.field("channel", channel)
|
||||||
.field("buffer", buffer)
|
.field("buffer", buffer)
|
||||||
.field("loops", loops)
|
.field("loops", loops)
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
PlayRcBufferOnChannel { channel, buffer, loops } => {
|
PlayRcBufferOnChannel { channel, buffer, loops } => {
|
||||||
f.debug_struct("PlayRcBufferOnChannel")
|
f.debug_struct("PlayRcBufferOnChannel") //
|
||||||
.field("channel", channel)
|
.field("channel", channel)
|
||||||
.field("buffer", buffer)
|
.field("buffer", buffer)
|
||||||
.field("loops", loops)
|
.field("loops", loops)
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
PlayGenerator { loops, .. } => {
|
PlayGenerator { loops, .. } => {
|
||||||
f.debug_struct("PlayGenerator")
|
f.debug_struct("PlayGenerator") //
|
||||||
.field("loops", loops)
|
.field("loops", loops)
|
||||||
.finish_non_exhaustive()
|
.finish_non_exhaustive()
|
||||||
}
|
}
|
||||||
PlayGeneratorOnChannel { channel, loops, .. } => {
|
PlayGeneratorOnChannel { channel, loops, .. } => {
|
||||||
f.debug_struct("PlayGeneratorOnChannel")
|
f.debug_struct("PlayGeneratorOnChannel") //
|
||||||
.field("channel", channel)
|
.field("channel", channel)
|
||||||
.field("loops", loops)
|
.field("loops", loops)
|
||||||
.finish_non_exhaustive()
|
.finish_non_exhaustive()
|
||||||
|
@ -98,10 +98,7 @@ pub struct AudioQueue {
|
||||||
impl AudioQueue {
|
impl AudioQueue {
|
||||||
/// Creates and returns a new [`AudioQueue`] instance.
|
/// Creates and returns a new [`AudioQueue`] instance.
|
||||||
pub fn new(audio: &Audio) -> Self {
|
pub fn new(audio: &Audio) -> Self {
|
||||||
AudioQueue {
|
AudioQueue { spec: audio.spec, commands: VecDeque::new() }
|
||||||
spec: audio.spec,
|
|
||||||
commands: VecDeque::new(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the spec that this queue is currently set to play. All audio to be played via
|
/// Returns the spec that this queue is currently set to play. All audio to be played via
|
||||||
|
@ -130,16 +127,12 @@ impl AudioQueue {
|
||||||
/// Queues a command to play a copy of the given [`AudioBuffer`]'s data. The buffer will be
|
/// 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
|
/// played on the first channel found that is not already playing. If all channels are already
|
||||||
/// playing, then nothing will be done.
|
/// playing, then nothing will be done.
|
||||||
pub fn play_buffer(
|
pub fn play_buffer(&mut self, buffer: &AudioBuffer, loops: bool) -> Result<(), AudioDeviceError> {
|
||||||
&mut self,
|
|
||||||
buffer: &AudioBuffer,
|
|
||||||
loops: bool,
|
|
||||||
) -> Result<(), AudioDeviceError> {
|
|
||||||
if *buffer.spec() != self.spec {
|
if *buffer.spec() != self.spec {
|
||||||
Err(AudioDeviceError::AudioSpecMismatch)
|
Err(AudioDeviceError::AudioSpecMismatch)
|
||||||
} else {
|
} else {
|
||||||
self.commands.push_back(AudioCommand::PlayBuffer {
|
self.commands.push_back(AudioCommand::PlayBuffer {
|
||||||
buffer: buffer.clone(),
|
buffer: buffer.clone(), //
|
||||||
loops,
|
loops,
|
||||||
});
|
});
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -150,18 +143,11 @@ impl AudioQueue {
|
||||||
/// the first channel found that is not already playing. If all channels are already playing,
|
/// 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`],
|
/// 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.
|
/// as that method will always immediately copy the given buffer to create the queued command.
|
||||||
pub fn play_buffer_rc(
|
pub fn play_buffer_rc(&mut self, buffer: Rc<AudioBuffer>, loops: bool) -> Result<(), AudioDeviceError> {
|
||||||
&mut self,
|
|
||||||
buffer: Rc<AudioBuffer>,
|
|
||||||
loops: bool,
|
|
||||||
) -> Result<(), AudioDeviceError> {
|
|
||||||
if *buffer.spec() != self.spec {
|
if *buffer.spec() != self.spec {
|
||||||
Err(AudioDeviceError::AudioSpecMismatch)
|
Err(AudioDeviceError::AudioSpecMismatch)
|
||||||
} else {
|
} else {
|
||||||
self.commands.push_back(AudioCommand::PlayRcBuffer {
|
self.commands.push_back(AudioCommand::PlayRcBuffer { buffer, loops });
|
||||||
buffer,
|
|
||||||
loops,
|
|
||||||
});
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -181,7 +167,7 @@ impl AudioQueue {
|
||||||
Err(AudioDeviceError::ChannelIndexOutOfRange(channel_index))
|
Err(AudioDeviceError::ChannelIndexOutOfRange(channel_index))
|
||||||
} else {
|
} else {
|
||||||
self.commands.push_back(AudioCommand::PlayBufferOnChannel {
|
self.commands.push_back(AudioCommand::PlayBufferOnChannel {
|
||||||
channel: channel_index,
|
channel: channel_index, //
|
||||||
buffer: buffer.clone(),
|
buffer: buffer.clone(),
|
||||||
loops,
|
loops,
|
||||||
});
|
});
|
||||||
|
@ -205,7 +191,7 @@ impl AudioQueue {
|
||||||
Err(AudioDeviceError::ChannelIndexOutOfRange(channel_index))
|
Err(AudioDeviceError::ChannelIndexOutOfRange(channel_index))
|
||||||
} else {
|
} else {
|
||||||
self.commands.push_back(AudioCommand::PlayRcBufferOnChannel {
|
self.commands.push_back(AudioCommand::PlayRcBufferOnChannel {
|
||||||
channel: channel_index,
|
channel: channel_index, //
|
||||||
buffer,
|
buffer,
|
||||||
loops,
|
loops,
|
||||||
});
|
});
|
||||||
|
@ -215,11 +201,7 @@ impl AudioQueue {
|
||||||
|
|
||||||
/// Queues a command to play the given [`AudioGenerator`] on the first channel found that is
|
/// 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.
|
/// not already playing. If all channels are already playing, then nothing will be done.
|
||||||
pub fn play_generator(
|
pub fn play_generator(&mut self, generator: Box<dyn AudioGenerator>, loops: bool) -> Result<(), AudioDeviceError> {
|
||||||
&mut self,
|
|
||||||
generator: Box<dyn AudioGenerator>,
|
|
||||||
loops: bool,
|
|
||||||
) -> Result<(), AudioDeviceError> {
|
|
||||||
self.commands.push_back(AudioCommand::PlayGenerator { generator, loops });
|
self.commands.push_back(AudioCommand::PlayGenerator { generator, loops });
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -233,7 +215,7 @@ impl AudioQueue {
|
||||||
loops: bool,
|
loops: bool,
|
||||||
) -> Result<(), AudioDeviceError> {
|
) -> Result<(), AudioDeviceError> {
|
||||||
self.commands.push_back(AudioCommand::PlayGeneratorOnChannel {
|
self.commands.push_back(AudioCommand::PlayGeneratorOnChannel {
|
||||||
channel: channel_index,
|
channel: channel_index, //
|
||||||
generator,
|
generator,
|
||||||
loops,
|
loops,
|
||||||
});
|
});
|
||||||
|
@ -286,4 +268,4 @@ impl AudioQueue {
|
||||||
let mut device = audio.lock();
|
let mut device = audio.lock();
|
||||||
self.apply_to_device(&mut device)
|
self.apply_to_device(&mut device)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,11 +134,13 @@ use thiserror::Error;
|
||||||
use crate::audio::device::AudioDeviceError;
|
use crate::audio::device::AudioDeviceError;
|
||||||
use crate::events::{EventListeners, EventPublisher};
|
use crate::events::{EventListeners, EventPublisher};
|
||||||
use crate::states::{AppState, StateError, States};
|
use crate::states::{AppState, StateError, States};
|
||||||
use crate::system::{System, SystemError};
|
|
||||||
use crate::system::res::SystemResources;
|
use crate::system::res::SystemResources;
|
||||||
|
use crate::system::{System, SystemError};
|
||||||
|
|
||||||
pub trait CoreState<SystemResType>
|
pub trait CoreState<SystemResType>
|
||||||
where SystemResType: SystemResources {
|
where
|
||||||
|
SystemResType: SystemResources,
|
||||||
|
{
|
||||||
fn system(&self) -> &System<SystemResType>;
|
fn system(&self) -> &System<SystemResType>;
|
||||||
fn system_mut(&mut self) -> &mut System<SystemResType>;
|
fn system_mut(&mut self) -> &mut System<SystemResType>;
|
||||||
|
|
||||||
|
@ -155,14 +157,17 @@ where SystemResType: SystemResources {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait CoreStateWithEvents<SystemResType, EventType>: CoreState<SystemResType>
|
pub trait CoreStateWithEvents<SystemResType, EventType>: CoreState<SystemResType>
|
||||||
where SystemResType: SystemResources {
|
where
|
||||||
|
SystemResType: SystemResources,
|
||||||
|
{
|
||||||
fn event_publisher(&mut self) -> &mut EventPublisher<EventType>;
|
fn event_publisher(&mut self) -> &mut EventPublisher<EventType>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait SupportSystems {}
|
pub trait SupportSystems {}
|
||||||
|
|
||||||
pub trait SupportSystemsWithEvents<SystemResType, EventType>: SupportSystems
|
pub trait SupportSystemsWithEvents<SystemResType, EventType>: SupportSystems
|
||||||
where SystemResType: SystemResources
|
where
|
||||||
|
SystemResType: SystemResources,
|
||||||
{
|
{
|
||||||
type ContextType: CoreStateWithEvents<SystemResType, EventType>;
|
type ContextType: CoreStateWithEvents<SystemResType, EventType>;
|
||||||
fn event_listeners(&mut self) -> &mut EventListeners<EventType, Self::ContextType>;
|
fn event_listeners(&mut self) -> &mut EventListeners<EventType, Self::ContextType>;
|
||||||
|
@ -174,7 +179,9 @@ where SystemResType: SystemResources
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait AppContext<SystemResType>
|
pub trait AppContext<SystemResType>
|
||||||
where SystemResType: SystemResources {
|
where
|
||||||
|
SystemResType: SystemResources,
|
||||||
|
{
|
||||||
type CoreType: CoreState<SystemResType>;
|
type CoreType: CoreState<SystemResType>;
|
||||||
type SupportType: SupportSystems;
|
type SupportType: SupportSystems;
|
||||||
|
|
||||||
|
|
|
@ -47,16 +47,12 @@ impl<T: Component> GenericComponentStore for ComponentStore<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn as_component_store<T: Component>(
|
pub fn as_component_store<T: Component>(collection: &dyn GenericComponentStore) -> &ComponentStore<T> {
|
||||||
collection: &dyn GenericComponentStore,
|
|
||||||
) -> &ComponentStore<T> {
|
|
||||||
collection.as_any().downcast_ref().unwrap()
|
collection.as_any().downcast_ref().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn as_component_store_mut<T: Component>(
|
pub fn as_component_store_mut<T: Component>(collection: &mut dyn GenericComponentStore) -> &mut ComponentStore<T> {
|
||||||
collection: &mut dyn GenericComponentStore,
|
|
||||||
) -> &mut ComponentStore<T> {
|
|
||||||
collection.as_any_mut().downcast_mut().unwrap()
|
collection.as_any_mut().downcast_mut().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,7 +79,7 @@ impl Entities {
|
||||||
/// Creates and returns a new instance of an entity manager.
|
/// Creates and returns a new instance of an entity manager.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Entities {
|
Entities {
|
||||||
entities: HashSet::new(),
|
entities: HashSet::new(), //
|
||||||
component_stores: HashMap::new(),
|
component_stores: HashMap::new(),
|
||||||
next_id: 0,
|
next_id: 0,
|
||||||
}
|
}
|
||||||
|
@ -100,9 +96,7 @@ impl Entities {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
let type_id = TypeId::of::<T>();
|
let type_id = TypeId::of::<T>();
|
||||||
Some(as_component_store(
|
Some(as_component_store(self.component_stores.get(&type_id).unwrap().as_ref()))
|
||||||
self.component_stores.get(&type_id).unwrap().as_ref(),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,8 +106,7 @@ impl Entities {
|
||||||
} else {
|
} else {
|
||||||
let component_store = ComponentStore::<T>::new(HashMap::new());
|
let component_store = ComponentStore::<T>::new(HashMap::new());
|
||||||
let type_id = TypeId::of::<T>();
|
let type_id = TypeId::of::<T>();
|
||||||
self.component_stores
|
self.component_stores.insert(type_id, Box::new(component_store));
|
||||||
.insert(type_id, Box::new(component_store));
|
|
||||||
as_component_store(self.component_stores.get(&type_id).unwrap().as_ref())
|
as_component_store(self.component_stores.get(&type_id).unwrap().as_ref())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -179,7 +172,7 @@ impl Entities {
|
||||||
if let Some(component_store) = self.get_component_store::<T>() {
|
if let Some(component_store) = self.get_component_store::<T>() {
|
||||||
component_store.borrow_mut().insert(entity, component);
|
component_store.borrow_mut().insert(entity, component);
|
||||||
} else {
|
} else {
|
||||||
self.add_component_store::<T>()
|
self.add_component_store::<T>() //
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.insert(entity, component);
|
.insert(entity, component);
|
||||||
}
|
}
|
||||||
|
@ -445,7 +438,7 @@ impl<U, R> std::fmt::Debug for ComponentSystems<U, R> {
|
||||||
impl<U, R> ComponentSystems<U, R> {
|
impl<U, R> ComponentSystems<U, R> {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
ComponentSystems {
|
ComponentSystems {
|
||||||
update_systems: Vec::new(),
|
update_systems: Vec::new(), //
|
||||||
render_systems: Vec::new(),
|
render_systems: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -735,10 +728,7 @@ mod tests {
|
||||||
let health = healths.get_mut(&entity);
|
let health = healths.get_mut(&entity);
|
||||||
let position = positions.get_mut(&entity);
|
let position = positions.get_mut(&entity);
|
||||||
|
|
||||||
println!(
|
println!("entity {}, health: {:?}, position: {:?}", name.0, health, position);
|
||||||
"entity {}, health: {:?}, position: {:?}",
|
|
||||||
name.0, health, position
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some(mut health) = health {
|
if let Some(mut health) = health {
|
||||||
health.0 += 5;
|
health.0 += 5;
|
||||||
|
@ -763,10 +753,7 @@ mod tests {
|
||||||
|
|
||||||
impl ComponentSystemContext {
|
impl ComponentSystemContext {
|
||||||
pub fn new(entities: Entities) -> Self {
|
pub fn new(entities: Entities) -> Self {
|
||||||
ComponentSystemContext {
|
ComponentSystemContext { delta: 0.0, entities }
|
||||||
delta: 0.0,
|
|
||||||
entities,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ pub struct EventPublisher<EventType> {
|
||||||
|
|
||||||
impl<EventType> std::fmt::Debug for EventPublisher<EventType> {
|
impl<EventType> std::fmt::Debug for EventPublisher<EventType> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
f.debug_struct("EventPublisher")
|
f.debug_struct("EventPublisher") //
|
||||||
.field("queue.len()", &self.queue.len())
|
.field("queue.len()", &self.queue.len())
|
||||||
.finish_non_exhaustive()
|
.finish_non_exhaustive()
|
||||||
}
|
}
|
||||||
|
@ -22,9 +22,7 @@ impl<EventType> std::fmt::Debug for EventPublisher<EventType> {
|
||||||
|
|
||||||
impl<EventType> EventPublisher<EventType> {
|
impl<EventType> EventPublisher<EventType> {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
EventPublisher {
|
EventPublisher { queue: VecDeque::new() }
|
||||||
queue: VecDeque::new(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the number of events that have been queued.
|
/// Returns the number of events that have been queued.
|
||||||
|
@ -78,7 +76,7 @@ impl<EventType, ContextType> std::fmt::Debug for EventListeners<EventType, Conte
|
||||||
impl<EventType, ContextType> EventListeners<EventType, ContextType> {
|
impl<EventType, ContextType> EventListeners<EventType, ContextType> {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
EventListeners {
|
EventListeners {
|
||||||
listeners: Vec::new(),
|
listeners: Vec::new(), //
|
||||||
dispatch_queue: VecDeque::new(),
|
dispatch_queue: VecDeque::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -193,7 +191,7 @@ mod tests {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => false
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -283,10 +281,7 @@ mod tests {
|
||||||
listeners.dispatch_queue(&mut context);
|
listeners.dispatch_queue(&mut context);
|
||||||
assert!(!context.events.is_empty());
|
assert!(!context.events.is_empty());
|
||||||
assert_eq!(0, context.count);
|
assert_eq!(0, context.count);
|
||||||
assert_eq!(
|
assert_eq!(vec![Dummy, Foobar(1), Dummy, Foobar(42)], context.events);
|
||||||
vec![Dummy, Foobar(1), Dummy, Foobar(42)],
|
|
||||||
context.events
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut context = TestContext::new();
|
let mut context = TestContext::new();
|
||||||
assert!(context.events.is_empty());
|
assert!(context.events.is_empty());
|
||||||
|
@ -303,10 +298,7 @@ mod tests {
|
||||||
listeners.dispatch_queue(&mut context);
|
listeners.dispatch_queue(&mut context);
|
||||||
assert!(!context.events.is_empty());
|
assert!(!context.events.is_empty());
|
||||||
assert_eq!(3, context.count);
|
assert_eq!(3, context.count);
|
||||||
assert_eq!(
|
assert_eq!(vec![Foobar(10), Foobar(20), Dummy], context.events);
|
||||||
vec![Foobar(10), Foobar(20), Dummy],
|
|
||||||
context.events
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -331,9 +323,6 @@ mod tests {
|
||||||
listeners.dispatch_queue(&mut context);
|
listeners.dispatch_queue(&mut context);
|
||||||
assert!(!context.events.is_empty());
|
assert!(!context.events.is_empty());
|
||||||
assert_eq!(3, context.count);
|
assert_eq!(3, context.count);
|
||||||
assert_eq!(
|
assert_eq!(vec![Message("hello"), Dummy, Foobar(3)], context.events);
|
||||||
vec![Message("hello"), Dummy, Foobar(3)],
|
|
||||||
context.events
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -255,14 +255,21 @@ pub unsafe fn per_pixel_rotozoom_blit<PixelType: Pixel>(
|
||||||
// destination bitmap's center point as the origin _except_ for the final post-transform
|
// 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
|
// 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!
|
// 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_x = ((x as f32 - half_dest_width) * cos * scale_x)
|
||||||
let src_y = ((x as f32 - half_dest_width) * sin * scale_y) + ((y as f32 - half_dest_height) * cos * scale_y) + half_src_height;
|
- ((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
|
// 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
|
// 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
|
// wrapping of course, since we just want to draw a single instance of this source
|
||||||
// bitmap.
|
// 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) {
|
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 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_x = x + dest_x;
|
||||||
|
@ -298,7 +305,13 @@ impl<PixelType: Pixel> Bitmap<PixelType> {
|
||||||
vertical_flip: bool,
|
vertical_flip: bool,
|
||||||
) {
|
) {
|
||||||
per_pixel_flipped_blit(
|
per_pixel_flipped_blit(
|
||||||
self, src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip,
|
self,
|
||||||
|
src,
|
||||||
|
src_region,
|
||||||
|
dest_x,
|
||||||
|
dest_y,
|
||||||
|
horizontal_flip,
|
||||||
|
vertical_flip,
|
||||||
|src_pixels, dest_pixels| {
|
|src_pixels, dest_pixels| {
|
||||||
*dest_pixels = *src_pixels;
|
*dest_pixels = *src_pixels;
|
||||||
},
|
},
|
||||||
|
@ -314,7 +327,11 @@ impl<PixelType: Pixel> Bitmap<PixelType> {
|
||||||
transparent_color: PixelType,
|
transparent_color: PixelType,
|
||||||
) {
|
) {
|
||||||
per_pixel_blit(
|
per_pixel_blit(
|
||||||
self, src, src_region, dest_x, dest_y,
|
self, //
|
||||||
|
src,
|
||||||
|
src_region,
|
||||||
|
dest_x,
|
||||||
|
dest_y,
|
||||||
|src_pixels, dest_pixels| {
|
|src_pixels, dest_pixels| {
|
||||||
if *src_pixels != transparent_color {
|
if *src_pixels != transparent_color {
|
||||||
*dest_pixels = *src_pixels;
|
*dest_pixels = *src_pixels;
|
||||||
|
@ -334,7 +351,13 @@ impl<PixelType: Pixel> Bitmap<PixelType> {
|
||||||
vertical_flip: bool,
|
vertical_flip: bool,
|
||||||
) {
|
) {
|
||||||
per_pixel_flipped_blit(
|
per_pixel_flipped_blit(
|
||||||
self, src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip,
|
self, //
|
||||||
|
src,
|
||||||
|
src_region,
|
||||||
|
dest_x,
|
||||||
|
dest_y,
|
||||||
|
horizontal_flip,
|
||||||
|
vertical_flip,
|
||||||
|src_pixels, dest_pixels| {
|
|src_pixels, dest_pixels| {
|
||||||
if *src_pixels != transparent_color {
|
if *src_pixels != transparent_color {
|
||||||
*dest_pixels = *src_pixels;
|
*dest_pixels = *src_pixels;
|
||||||
|
@ -353,7 +376,11 @@ impl<PixelType: Pixel> Bitmap<PixelType> {
|
||||||
draw_color: PixelType,
|
draw_color: PixelType,
|
||||||
) {
|
) {
|
||||||
per_pixel_blit(
|
per_pixel_blit(
|
||||||
self, src, src_region, dest_x, dest_y,
|
self, //
|
||||||
|
src,
|
||||||
|
src_region,
|
||||||
|
dest_x,
|
||||||
|
dest_y,
|
||||||
|src_pixels, dest_pixels| {
|
|src_pixels, dest_pixels| {
|
||||||
if *src_pixels != transparent_color {
|
if *src_pixels != transparent_color {
|
||||||
*dest_pixels = draw_color;
|
*dest_pixels = draw_color;
|
||||||
|
@ -374,7 +401,13 @@ impl<PixelType: Pixel> Bitmap<PixelType> {
|
||||||
draw_color: PixelType,
|
draw_color: PixelType,
|
||||||
) {
|
) {
|
||||||
per_pixel_flipped_blit(
|
per_pixel_flipped_blit(
|
||||||
self, src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip,
|
self, //
|
||||||
|
src,
|
||||||
|
src_region,
|
||||||
|
dest_x,
|
||||||
|
dest_y,
|
||||||
|
horizontal_flip,
|
||||||
|
vertical_flip,
|
||||||
|src_pixels, dest_pixels| {
|
|src_pixels, dest_pixels| {
|
||||||
if *src_pixels != transparent_color {
|
if *src_pixels != transparent_color {
|
||||||
*dest_pixels = draw_color;
|
*dest_pixels = draw_color;
|
||||||
|
@ -394,7 +427,14 @@ impl<PixelType: Pixel> Bitmap<PixelType> {
|
||||||
scale_y: f32,
|
scale_y: f32,
|
||||||
) {
|
) {
|
||||||
per_pixel_rotozoom_blit(
|
per_pixel_rotozoom_blit(
|
||||||
self, src, src_region, dest_x, dest_y, angle, scale_x, scale_y,
|
self, //
|
||||||
|
src,
|
||||||
|
src_region,
|
||||||
|
dest_x,
|
||||||
|
dest_y,
|
||||||
|
angle,
|
||||||
|
scale_x,
|
||||||
|
scale_y,
|
||||||
|src_pixel, dest_bitmap, draw_x, draw_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, draw_y, src_pixel);
|
||||||
},
|
},
|
||||||
|
@ -413,7 +453,14 @@ impl<PixelType: Pixel> Bitmap<PixelType> {
|
||||||
transparent_color: PixelType,
|
transparent_color: PixelType,
|
||||||
) {
|
) {
|
||||||
per_pixel_rotozoom_blit(
|
per_pixel_rotozoom_blit(
|
||||||
self, src, src_region, dest_x, dest_y, angle, scale_x, scale_y,
|
self, //
|
||||||
|
src,
|
||||||
|
src_region,
|
||||||
|
dest_x,
|
||||||
|
dest_y,
|
||||||
|
angle,
|
||||||
|
scale_x,
|
||||||
|
scale_y,
|
||||||
|src_pixel, dest_bitmap, draw_x, draw_y| {
|
|src_pixel, dest_bitmap, draw_x, draw_y| {
|
||||||
if transparent_color != src_pixel {
|
if transparent_color != src_pixel {
|
||||||
dest_bitmap.set_pixel(draw_x, draw_y, src_pixel);
|
dest_bitmap.set_pixel(draw_x, draw_y, src_pixel);
|
||||||
|
|
|
@ -6,11 +6,11 @@
|
||||||
//!
|
//!
|
||||||
//! Only a subset of the most common Bitmap drawing operations will be provided here.
|
//! Only a subset of the most common Bitmap drawing operations will be provided here.
|
||||||
|
|
||||||
use crate::graphics::bitmap::BitmapError;
|
|
||||||
use crate::graphics::bitmap::indexed::blit::IndexedBlitMethod;
|
use crate::graphics::bitmap::indexed::blit::IndexedBlitMethod;
|
||||||
use crate::graphics::bitmap::indexed::IndexedBitmap;
|
use crate::graphics::bitmap::indexed::IndexedBitmap;
|
||||||
use crate::graphics::bitmap::rgb::blit::RgbaBlitMethod;
|
use crate::graphics::bitmap::rgb::blit::RgbaBlitMethod;
|
||||||
use crate::graphics::bitmap::rgb::RgbaBitmap;
|
use crate::graphics::bitmap::rgb::RgbaBitmap;
|
||||||
|
use crate::graphics::bitmap::BitmapError;
|
||||||
use crate::graphics::Pixel;
|
use crate::graphics::Pixel;
|
||||||
use crate::math::rect::Rect;
|
use crate::math::rect::Rect;
|
||||||
|
|
||||||
|
@ -87,7 +87,7 @@ pub trait GeneralBitmap: Sized + Clone {
|
||||||
src: &Self,
|
src: &Self,
|
||||||
src_region: &Rect,
|
src_region: &Rect,
|
||||||
dest_x: i32,
|
dest_x: i32,
|
||||||
dest_y: i32
|
dest_y: i32,
|
||||||
);
|
);
|
||||||
|
|
||||||
fn blit(&mut self, method: GeneralBlitMethod<Self::PixelType>, src: &Self, x: i32, y: i32) {
|
fn blit(&mut self, method: GeneralBlitMethod<Self::PixelType>, src: &Self, x: i32, y: i32) {
|
||||||
|
@ -165,7 +165,7 @@ impl GeneralBitmap for IndexedBitmap {
|
||||||
src: &Self,
|
src: &Self,
|
||||||
src_region: &Rect,
|
src_region: &Rect,
|
||||||
dest_x: i32,
|
dest_x: i32,
|
||||||
dest_y: i32
|
dest_y: i32,
|
||||||
) {
|
) {
|
||||||
let blit_method = match method {
|
let blit_method = match method {
|
||||||
GeneralBlitMethod::Solid => IndexedBlitMethod::Solid,
|
GeneralBlitMethod::Solid => IndexedBlitMethod::Solid,
|
||||||
|
@ -244,7 +244,7 @@ impl GeneralBitmap for RgbaBitmap {
|
||||||
src: &Self,
|
src: &Self,
|
||||||
src_region: &Rect,
|
src_region: &Rect,
|
||||||
dest_x: i32,
|
dest_x: i32,
|
||||||
dest_y: i32
|
dest_y: i32,
|
||||||
) {
|
) {
|
||||||
let blit_method = match method {
|
let blit_method = match method {
|
||||||
GeneralBlitMethod::Solid => RgbaBlitMethod::Solid,
|
GeneralBlitMethod::Solid => RgbaBlitMethod::Solid,
|
||||||
|
@ -252,4 +252,4 @@ impl GeneralBitmap for RgbaBitmap {
|
||||||
};
|
};
|
||||||
self.blit_region(blit_method, src, src_region, dest_x, dest_y)
|
self.blit_region(blit_method, src, src_region, dest_x, dest_y)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ use crate::graphics::bitmap::rgb::RgbaBitmap;
|
||||||
use crate::graphics::palette::{Palette, PaletteError, PaletteFormat};
|
use crate::graphics::palette::{Palette, PaletteError, PaletteFormat};
|
||||||
use crate::utils::lzwgif::{lzw_decode, lzw_encode, LzwError};
|
use crate::utils::lzwgif::{lzw_decode, lzw_encode, LzwError};
|
||||||
|
|
||||||
const BITS_FOR_256_COLORS: u32 = 7; // formula is `2 ^ (bits + 1) = num_colors`
|
const BITS_FOR_256_COLORS: u32 = 7; // formula is `2 ^ (bits + 1) = num_colors`
|
||||||
|
|
||||||
fn bits_to_num_colors(bits: u32) -> u32 {
|
fn bits_to_num_colors(bits: u32) -> u32 {
|
||||||
1_u32.wrapping_shl(bits + 1)
|
1_u32.wrapping_shl(bits + 1)
|
||||||
|
@ -119,7 +119,7 @@ impl GifHeader {
|
||||||
let mut version = [0u8; 3];
|
let mut version = [0u8; 3];
|
||||||
reader.read_exact(&mut version)?;
|
reader.read_exact(&mut version)?;
|
||||||
Ok(GifHeader {
|
Ok(GifHeader {
|
||||||
signature,
|
signature, //
|
||||||
version,
|
version,
|
||||||
screen_width: reader.read_u16::<LittleEndian>()?,
|
screen_width: reader.read_u16::<LittleEndian>()?,
|
||||||
screen_height: reader.read_u16::<LittleEndian>()?,
|
screen_height: reader.read_u16::<LittleEndian>()?,
|
||||||
|
@ -161,7 +161,7 @@ impl GifExtensionLabel {
|
||||||
0x01 => Ok(PlainText),
|
0x01 => Ok(PlainText),
|
||||||
0xff => Ok(Application),
|
0xff => Ok(Application),
|
||||||
0xfe => Ok(Comment),
|
0xfe => Ok(Comment),
|
||||||
_ => Err(GifError::UnknownExtension(value))
|
_ => Err(GifError::UnknownExtension(value)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -178,7 +178,7 @@ struct GraphicControlExtension {
|
||||||
impl GraphicControlExtension {
|
impl GraphicControlExtension {
|
||||||
pub fn read<T: Read>(reader: &mut T) -> Result<Self, GifError> {
|
pub fn read<T: Read>(reader: &mut T) -> Result<Self, GifError> {
|
||||||
Ok(GraphicControlExtension {
|
Ok(GraphicControlExtension {
|
||||||
block_size: reader.read_u8()?,
|
block_size: reader.read_u8()?, //
|
||||||
flags: reader.read_u8()?,
|
flags: reader.read_u8()?,
|
||||||
delay: reader.read_u16::<LittleEndian>()?,
|
delay: reader.read_u16::<LittleEndian>()?,
|
||||||
transparent_color: reader.read_u8()?,
|
transparent_color: reader.read_u8()?,
|
||||||
|
@ -259,7 +259,7 @@ impl ApplicationExtension {
|
||||||
let mut authentication_code = [0u8; 3];
|
let mut authentication_code = [0u8; 3];
|
||||||
reader.read_exact(&mut authentication_code)?;
|
reader.read_exact(&mut authentication_code)?;
|
||||||
Ok(ApplicationExtension {
|
Ok(ApplicationExtension {
|
||||||
block_size,
|
block_size, //
|
||||||
identifier,
|
identifier,
|
||||||
authentication_code,
|
authentication_code,
|
||||||
data: read_raw_sub_block_data(reader)?,
|
data: read_raw_sub_block_data(reader)?,
|
||||||
|
@ -283,9 +283,7 @@ struct CommentExtension {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
impl CommentExtension {
|
impl CommentExtension {
|
||||||
pub fn read<T: Read>(reader: &mut T) -> Result<Self, GifError> {
|
pub fn read<T: Read>(reader: &mut T) -> Result<Self, GifError> {
|
||||||
Ok(CommentExtension {
|
Ok(CommentExtension { data: read_raw_sub_block_data(reader)? })
|
||||||
data: read_raw_sub_block_data(reader)?,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write<T: Write>(&self, writer: &mut T) -> Result<(), GifError> {
|
pub fn write<T: Write>(&self, writer: &mut T) -> Result<(), GifError> {
|
||||||
|
@ -331,7 +329,7 @@ impl LocalImageDescriptor {
|
||||||
|
|
||||||
pub fn read<T: Read>(reader: &mut T) -> Result<Self, GifError> {
|
pub fn read<T: Read>(reader: &mut T) -> Result<Self, GifError> {
|
||||||
Ok(LocalImageDescriptor {
|
Ok(LocalImageDescriptor {
|
||||||
x: reader.read_u16::<LittleEndian>()?,
|
x: reader.read_u16::<LittleEndian>()?, //
|
||||||
y: reader.read_u16::<LittleEndian>()?,
|
y: reader.read_u16::<LittleEndian>()?,
|
||||||
width: reader.read_u16::<LittleEndian>()?,
|
width: reader.read_u16::<LittleEndian>()?,
|
||||||
height: reader.read_u16::<LittleEndian>()?,
|
height: reader.read_u16::<LittleEndian>()?,
|
||||||
|
@ -359,11 +357,7 @@ fn load_image_section<T: ReadBytesExt>(
|
||||||
let palette: Option<Palette>;
|
let palette: Option<Palette>;
|
||||||
if descriptor.has_local_color_table() {
|
if descriptor.has_local_color_table() {
|
||||||
let num_colors = bits_to_num_colors(descriptor.local_color_table_bits() as u32) as usize;
|
let num_colors = bits_to_num_colors(descriptor.local_color_table_bits() as u32) as usize;
|
||||||
palette = Some(Palette::load_num_colors_from_bytes(
|
palette = Some(Palette::load_num_colors_from_bytes(reader, PaletteFormat::Normal, num_colors)?);
|
||||||
reader,
|
|
||||||
PaletteFormat::Normal,
|
|
||||||
num_colors,
|
|
||||||
)?);
|
|
||||||
} else {
|
} else {
|
||||||
palette = None; // we expect that there was a global color table previously
|
palette = None; // we expect that there was a global color table previously
|
||||||
}
|
}
|
||||||
|
@ -375,17 +369,14 @@ fn load_image_section<T: ReadBytesExt>(
|
||||||
Ok((bitmap, palette))
|
Ok((bitmap, palette))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn save_image_section<T: WriteBytesExt>(
|
fn save_image_section<T: WriteBytesExt>(writer: &mut T, bitmap: &IndexedBitmap) -> Result<(), GifError> {
|
||||||
writer: &mut T,
|
|
||||||
bitmap: &IndexedBitmap,
|
|
||||||
) -> Result<(), GifError> {
|
|
||||||
writer.write_u8(IMAGE_DESCRIPTOR_SEPARATOR)?;
|
writer.write_u8(IMAGE_DESCRIPTOR_SEPARATOR)?;
|
||||||
let image_descriptor = LocalImageDescriptor {
|
let image_descriptor = LocalImageDescriptor {
|
||||||
x: 0,
|
x: 0, //
|
||||||
y: 0,
|
y: 0,
|
||||||
width: bitmap.width as u16,
|
width: bitmap.width as u16,
|
||||||
height: bitmap.height as u16,
|
height: bitmap.height as u16,
|
||||||
flags: 0, // again, we're not using local color tables, so no flags to set here
|
flags: 0, // again, we're not using local color tables, so no flags to set here
|
||||||
};
|
};
|
||||||
image_descriptor.write(writer)?;
|
image_descriptor.write(writer)?;
|
||||||
|
|
||||||
|
@ -400,9 +391,7 @@ fn save_image_section<T: WriteBytesExt>(
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IndexedBitmap {
|
impl IndexedBitmap {
|
||||||
pub fn load_gif_bytes<T: ReadBytesExt>(
|
pub fn load_gif_bytes<T: ReadBytesExt>(reader: &mut T) -> Result<(IndexedBitmap, Palette), GifError> {
|
||||||
reader: &mut T,
|
|
||||||
) -> Result<(IndexedBitmap, Palette), GifError> {
|
|
||||||
let header = GifHeader::read(reader)?;
|
let header = GifHeader::read(reader)?;
|
||||||
if header.signature != *b"GIF" || header.version != *b"89a" {
|
if header.signature != *b"GIF" || header.version != *b"89a" {
|
||||||
return Err(GifError::BadFile(String::from("Expected GIF89a header signature")));
|
return Err(GifError::BadFile(String::from("Expected GIF89a header signature")));
|
||||||
|
@ -412,11 +401,7 @@ impl IndexedBitmap {
|
||||||
let mut palette: Option<Palette>;
|
let mut palette: Option<Palette>;
|
||||||
if header.has_global_color_table() {
|
if header.has_global_color_table() {
|
||||||
let num_colors = bits_to_num_colors(header.global_color_table_bits() as u32) as usize;
|
let num_colors = bits_to_num_colors(header.global_color_table_bits() as u32) as usize;
|
||||||
palette = Some(Palette::load_num_colors_from_bytes(
|
palette = Some(Palette::load_num_colors_from_bytes(reader, PaletteFormat::Normal, num_colors)?);
|
||||||
reader,
|
|
||||||
PaletteFormat::Normal,
|
|
||||||
num_colors,
|
|
||||||
)?);
|
|
||||||
} else {
|
} else {
|
||||||
palette = None; // we expect to find a local color table later
|
palette = None; // we expect to find a local color table later
|
||||||
}
|
}
|
||||||
|
@ -468,7 +453,10 @@ impl IndexedBitmap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(GifError::BadFile(format!("Unexpected byte found {} not a file trailer, image separator or extension introducer", current_byte)));
|
return Err(GifError::BadFile(format!(
|
||||||
|
"Unexpected byte found {} not a file trailer, image separator or extension introducer",
|
||||||
|
current_byte
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -526,7 +514,7 @@ impl IndexedBitmap {
|
||||||
writer.write_u8(EXTENSION_INTRODUCER)?;
|
writer.write_u8(EXTENSION_INTRODUCER)?;
|
||||||
writer.write_u8(GifExtensionLabel::GraphicControl as u8)?;
|
writer.write_u8(GifExtensionLabel::GraphicControl as u8)?;
|
||||||
let graphic_control = GraphicControlExtension {
|
let graphic_control = GraphicControlExtension {
|
||||||
block_size: 4,
|
block_size: 4, //
|
||||||
flags: 0,
|
flags: 0,
|
||||||
delay: 0,
|
delay: 0,
|
||||||
transparent_color,
|
transparent_color,
|
||||||
|
@ -540,12 +528,7 @@ impl IndexedBitmap {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_gif_file(
|
pub fn to_gif_file(&self, path: &Path, palette: &Palette, settings: GifSettings) -> Result<(), GifError> {
|
||||||
&self,
|
|
||||||
path: &Path,
|
|
||||||
palette: &Palette,
|
|
||||||
settings: GifSettings,
|
|
||||||
) -> Result<(), GifError> {
|
|
||||||
let f = File::create(path)?;
|
let f = File::create(path)?;
|
||||||
let mut writer = BufWriter::new(f);
|
let mut writer = BufWriter::new(f);
|
||||||
self.to_gif_bytes(&mut writer, palette, settings)
|
self.to_gif_bytes(&mut writer, palette, settings)
|
||||||
|
@ -556,9 +539,7 @@ impl IndexedBitmap {
|
||||||
// multi-pixel-depth support.
|
// multi-pixel-depth support.
|
||||||
|
|
||||||
impl RgbaBitmap {
|
impl RgbaBitmap {
|
||||||
pub fn load_gif_bytes<T: ReadBytesExt>(
|
pub fn load_gif_bytes<T: ReadBytesExt>(reader: &mut T) -> Result<(RgbaBitmap, Palette), GifError> {
|
||||||
reader: &mut T,
|
|
||||||
) -> Result<(RgbaBitmap, Palette), GifError> {
|
|
||||||
let (temp_bitmap, palette) = IndexedBitmap::load_gif_bytes(reader)?;
|
let (temp_bitmap, palette) = IndexedBitmap::load_gif_bytes(reader)?;
|
||||||
let output = temp_bitmap.to_rgba(&palette);
|
let output = temp_bitmap.to_rgba(&palette);
|
||||||
Ok((output, palette))
|
Ok((output, palette))
|
||||||
|
@ -593,9 +574,10 @@ pub mod tests {
|
||||||
|
|
||||||
let ref_pixels = load_raw_indexed(test_file(Path::new("small.bin")).as_path())?;
|
let ref_pixels = load_raw_indexed(test_file(Path::new("small.bin")).as_path())?;
|
||||||
let dp2_palette = Palette::load_from_file(
|
let dp2_palette = Palette::load_from_file(
|
||||||
test_assets_file(Path::new("dp2.pal")).as_path(),
|
test_assets_file(Path::new("dp2.pal")).as_path(), //
|
||||||
PaletteFormat::Normal,
|
PaletteFormat::Normal,
|
||||||
).unwrap();
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let (bmp, palette) = IndexedBitmap::load_gif_file(test_file(Path::new("small.gif")).as_path())?;
|
let (bmp, palette) = IndexedBitmap::load_gif_file(test_file(Path::new("small.gif")).as_path())?;
|
||||||
assert_eq!(16, bmp.width());
|
assert_eq!(16, bmp.width());
|
||||||
|
|
|
@ -9,7 +9,7 @@ use thiserror::Error;
|
||||||
use crate::graphics::bitmap::indexed::IndexedBitmap;
|
use crate::graphics::bitmap::indexed::IndexedBitmap;
|
||||||
use crate::graphics::bitmap::rgb::RgbaBitmap;
|
use crate::graphics::bitmap::rgb::RgbaBitmap;
|
||||||
use crate::graphics::palette::{Palette, PaletteError, PaletteFormat};
|
use crate::graphics::palette::{Palette, PaletteError, PaletteFormat};
|
||||||
use crate::utils::packbits::{pack_bits, PackBitsError, unpack_bits};
|
use crate::utils::packbits::{pack_bits, unpack_bits, PackBitsError};
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum IffError {
|
pub enum IffError {
|
||||||
|
@ -91,11 +91,7 @@ impl FormChunkHeader {
|
||||||
let chunk_id = IffId::read(reader)?;
|
let chunk_id = IffId::read(reader)?;
|
||||||
let size = reader.read_u32::<BigEndian>()?;
|
let size = reader.read_u32::<BigEndian>()?;
|
||||||
let type_id = IffId::read(reader)?;
|
let type_id = IffId::read(reader)?;
|
||||||
Ok(FormChunkHeader {
|
Ok(FormChunkHeader { chunk_id, size, type_id })
|
||||||
chunk_id,
|
|
||||||
size,
|
|
||||||
type_id,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write<T: WriteBytesExt>(&self, writer: &mut T) -> Result<(), IffError> {
|
pub fn write<T: WriteBytesExt>(&self, writer: &mut T) -> Result<(), IffError> {
|
||||||
|
@ -287,7 +283,7 @@ fn load_planar_body<T: ReadBytesExt>(reader: &mut T, bmhd: &BMHDChunk) -> Result
|
||||||
// scanline, the destination pointer will contain VGA-friendly
|
// scanline, the destination pointer will contain VGA-friendly
|
||||||
// "chunky pixel"-format pixel data
|
// "chunky pixel"-format pixel data
|
||||||
merge_bitplane(
|
merge_bitplane(
|
||||||
plane,
|
plane, //
|
||||||
&buffer,
|
&buffer,
|
||||||
bitmap.pixels_at_mut(0, y as i32).unwrap(),
|
bitmap.pixels_at_mut(0, y as i32).unwrap(),
|
||||||
row_bytes,
|
row_bytes,
|
||||||
|
@ -327,7 +323,7 @@ fn write_planar_body<T: WriteBytesExt>(
|
||||||
for y in 0..bitmap.height() {
|
for y in 0..bitmap.height() {
|
||||||
for plane in 0..(bmhd.bitplanes as u32) {
|
for plane in 0..(bmhd.bitplanes as u32) {
|
||||||
extract_bitplane(
|
extract_bitplane(
|
||||||
plane,
|
plane, //
|
||||||
bitmap.pixels_at(0, y as i32).unwrap(),
|
bitmap.pixels_at(0, y as i32).unwrap(),
|
||||||
&mut buffer,
|
&mut buffer,
|
||||||
row_bytes,
|
row_bytes,
|
||||||
|
@ -369,19 +365,13 @@ fn write_chunky_body<T: WriteBytesExt>(
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IndexedBitmap {
|
impl IndexedBitmap {
|
||||||
pub fn load_iff_bytes<T: ReadBytesExt + Seek>(
|
pub fn load_iff_bytes<T: ReadBytesExt + Seek>(reader: &mut T) -> Result<(IndexedBitmap, Palette), IffError> {
|
||||||
reader: &mut T,
|
|
||||||
) -> Result<(IndexedBitmap, Palette), IffError> {
|
|
||||||
let form_chunk = FormChunkHeader::read(reader)?;
|
let form_chunk = FormChunkHeader::read(reader)?;
|
||||||
if form_chunk.chunk_id.id != *b"FORM" {
|
if form_chunk.chunk_id.id != *b"FORM" {
|
||||||
return Err(IffError::BadFile(String::from(
|
return Err(IffError::BadFile(String::from("Unexpected form chunk ID, probably not an IFF file")));
|
||||||
"Unexpected form chunk ID, probably not an IFF file",
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
if form_chunk.type_id.id != *b"ILBM" && form_chunk.type_id.id != *b"PBM " {
|
if form_chunk.type_id.id != *b"ILBM" && form_chunk.type_id.id != *b"PBM " {
|
||||||
return Err(IffError::BadFile(String::from(
|
return Err(IffError::BadFile(String::from("Only ILBM or PBM formats are supported")));
|
||||||
"Only ILBM or PBM formats are supported",
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut bmhd: Option<BMHDChunk> = None;
|
let mut bmhd: Option<BMHDChunk> = None;
|
||||||
|
@ -391,11 +381,9 @@ impl IndexedBitmap {
|
||||||
loop {
|
loop {
|
||||||
let header = match SubChunkHeader::read(reader) {
|
let header = match SubChunkHeader::read(reader) {
|
||||||
Ok(header) => header,
|
Ok(header) => header,
|
||||||
Err(IffError::IOError(io_error))
|
Err(IffError::IOError(io_error)) if io_error.kind() == io::ErrorKind::UnexpectedEof => {
|
||||||
if io_error.kind() == io::ErrorKind::UnexpectedEof =>
|
break;
|
||||||
{
|
}
|
||||||
break;
|
|
||||||
}
|
|
||||||
Err(err) => return Err(err),
|
Err(err) => return Err(err),
|
||||||
};
|
};
|
||||||
let chunk_data_position = reader.stream_position()?;
|
let chunk_data_position = reader.stream_position()?;
|
||||||
|
@ -404,18 +392,14 @@ impl IndexedBitmap {
|
||||||
if header.chunk_id.id == *b"BMHD" {
|
if header.chunk_id.id == *b"BMHD" {
|
||||||
bmhd = Some(BMHDChunk::read(reader)?);
|
bmhd = Some(BMHDChunk::read(reader)?);
|
||||||
if bmhd.as_ref().unwrap().bitplanes != 8 {
|
if bmhd.as_ref().unwrap().bitplanes != 8 {
|
||||||
return Err(IffError::BadFile(String::from(
|
return Err(IffError::BadFile(String::from("Only 8bpp files are supported")));
|
||||||
"Only 8bpp files are supported",
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
if bmhd.as_ref().unwrap().masking == 1 {
|
if bmhd.as_ref().unwrap().masking == 1 {
|
||||||
return Err(IffError::BadFile(String::from("Masking is not supported")));
|
return Err(IffError::BadFile(String::from("Masking is not supported")));
|
||||||
}
|
}
|
||||||
} else if header.chunk_id.id == *b"CMAP" {
|
} else if header.chunk_id.id == *b"CMAP" {
|
||||||
if header.size != 768 {
|
if header.size != 768 {
|
||||||
return Err(IffError::BadFile(String::from(
|
return Err(IffError::BadFile(String::from("Only 256 color files are supported")));
|
||||||
"Only 256 color files are supported",
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
palette = Some(Palette::load_from_bytes(reader, PaletteFormat::Normal)?)
|
palette = Some(Palette::load_from_bytes(reader, PaletteFormat::Normal)?)
|
||||||
} else if header.chunk_id.id == *b"BODY" {
|
} else if header.chunk_id.id == *b"BODY" {
|
||||||
|
@ -464,22 +448,16 @@ impl IndexedBitmap {
|
||||||
|
|
||||||
let mut form_chunk = FormChunkHeader {
|
let mut form_chunk = FormChunkHeader {
|
||||||
chunk_id: IffId { id: *b"FORM" },
|
chunk_id: IffId { id: *b"FORM" },
|
||||||
type_id: IffId {
|
type_id: IffId { id: format.type_id() },
|
||||||
id: format.type_id(),
|
|
||||||
},
|
|
||||||
size: 0, // filled in later once we know the size
|
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
|
// skip over the form chunk for now. will come back here and write it out later once we
|
||||||
// know what the final size is
|
// know what the final size is
|
||||||
writer.seek(SeekFrom::Current(
|
writer.seek(SeekFrom::Current(std::mem::size_of::<FormChunkHeader>() as i64))?;
|
||||||
std::mem::size_of::<FormChunkHeader>() as i64
|
|
||||||
))?;
|
|
||||||
|
|
||||||
let bmhd_chunk_header = SubChunkHeader {
|
let bmhd_chunk_header =
|
||||||
chunk_id: IffId { id: *b"BMHD" },
|
SubChunkHeader { chunk_id: IffId { id: *b"BMHD" }, size: std::mem::size_of::<BMHDChunk>() as u32 };
|
||||||
size: std::mem::size_of::<BMHDChunk>() as u32,
|
|
||||||
};
|
|
||||||
let bmhd = BMHDChunk {
|
let bmhd = BMHDChunk {
|
||||||
width: self.width() as u16,
|
width: self.width() as u16,
|
||||||
height: self.height() as u16,
|
height: self.height() as u16,
|
||||||
|
@ -501,7 +479,7 @@ impl IndexedBitmap {
|
||||||
|
|
||||||
let cmap_chunk_header = SubChunkHeader {
|
let cmap_chunk_header = SubChunkHeader {
|
||||||
chunk_id: IffId { id: *b"CMAP" },
|
chunk_id: IffId { id: *b"CMAP" },
|
||||||
size: 768,
|
size: 768, //
|
||||||
};
|
};
|
||||||
cmap_chunk_header.write(writer)?;
|
cmap_chunk_header.write(writer)?;
|
||||||
palette.to_bytes(writer, PaletteFormat::Normal)?;
|
palette.to_bytes(writer, PaletteFormat::Normal)?;
|
||||||
|
@ -515,9 +493,7 @@ impl IndexedBitmap {
|
||||||
|
|
||||||
// skip over the body chunk header for now. we will again come back here and write it out
|
// 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.
|
// later once we know what the final size again.
|
||||||
writer.seek(SeekFrom::Current(
|
writer.seek(SeekFrom::Current(std::mem::size_of::<SubChunkHeader>() as i64))?;
|
||||||
std::mem::size_of::<SubChunkHeader>() as i64
|
|
||||||
))?;
|
|
||||||
|
|
||||||
if format.chunky() {
|
if format.chunky() {
|
||||||
write_chunky_body(writer, self, &bmhd)?;
|
write_chunky_body(writer, self, &bmhd)?;
|
||||||
|
@ -548,12 +524,7 @@ impl IndexedBitmap {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_iff_file(
|
pub fn to_iff_file(&self, path: &Path, palette: &Palette, format: IffFormat) -> Result<(), IffError> {
|
||||||
&self,
|
|
||||||
path: &Path,
|
|
||||||
palette: &Palette,
|
|
||||||
format: IffFormat,
|
|
||||||
) -> Result<(), IffError> {
|
|
||||||
let f = File::create(path)?;
|
let f = File::create(path)?;
|
||||||
let mut writer = BufWriter::new(f);
|
let mut writer = BufWriter::new(f);
|
||||||
self.to_iff_bytes(&mut writer, palette, format)
|
self.to_iff_bytes(&mut writer, palette, format)
|
||||||
|
@ -564,9 +535,7 @@ impl IndexedBitmap {
|
||||||
// multi-pixel-depth support.
|
// multi-pixel-depth support.
|
||||||
|
|
||||||
impl RgbaBitmap {
|
impl RgbaBitmap {
|
||||||
pub fn load_iff_bytes<T: ReadBytesExt + Seek>(
|
pub fn load_iff_bytes<T: ReadBytesExt + Seek>(reader: &mut T) -> Result<(RgbaBitmap, Palette), IffError> {
|
||||||
reader: &mut T,
|
|
||||||
) -> Result<(RgbaBitmap, Palette), IffError> {
|
|
||||||
let (temp_bitmap, palette) = IndexedBitmap::load_iff_bytes(reader)?;
|
let (temp_bitmap, palette) = IndexedBitmap::load_iff_bytes(reader)?;
|
||||||
let output = temp_bitmap.to_rgba(&palette);
|
let output = temp_bitmap.to_rgba(&palette);
|
||||||
Ok((output, palette))
|
Ok((output, palette))
|
||||||
|
@ -601,9 +570,10 @@ mod tests {
|
||||||
|
|
||||||
let ref_pixels = load_raw_indexed(test_file(Path::new("small.bin")).as_path())?;
|
let ref_pixels = load_raw_indexed(test_file(Path::new("small.bin")).as_path())?;
|
||||||
let dp2_palette = Palette::load_from_file(
|
let dp2_palette = Palette::load_from_file(
|
||||||
test_assets_file(Path::new("dp2.pal")).as_path(),
|
test_assets_file(Path::new("dp2.pal")).as_path(), //
|
||||||
PaletteFormat::Normal
|
PaletteFormat::Normal,
|
||||||
).unwrap();
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
// ILBM format
|
// ILBM format
|
||||||
|
|
||||||
|
|
|
@ -68,7 +68,10 @@ pub enum IndexedBlitMethod {
|
||||||
/// Same as [IndexedBlitMethod::Transparent] except that the drawn pixels have their color indices
|
/// Same as [IndexedBlitMethod::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
|
/// 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.
|
/// is always treated as an absolute palette color index.
|
||||||
TransparentOffset { transparent_color: u8, offset: u8 },
|
TransparentOffset {
|
||||||
|
transparent_color: u8,
|
||||||
|
offset: u8,
|
||||||
|
},
|
||||||
/// Combination of [IndexedBlitMethod::TransparentFlipped] and [IndexedBlitMethod::TransparentOffset].
|
/// Combination of [IndexedBlitMethod::TransparentFlipped] and [IndexedBlitMethod::TransparentOffset].
|
||||||
TransparentFlippedOffset {
|
TransparentFlippedOffset {
|
||||||
transparent_color: u8,
|
transparent_color: u8,
|
||||||
|
@ -133,7 +136,11 @@ impl IndexedBitmap {
|
||||||
blend_map: Rc<BlendMap>,
|
blend_map: Rc<BlendMap>,
|
||||||
) {
|
) {
|
||||||
per_pixel_blit(
|
per_pixel_blit(
|
||||||
self, src, src_region, dest_x, dest_y,
|
self, //
|
||||||
|
src,
|
||||||
|
src_region,
|
||||||
|
dest_x,
|
||||||
|
dest_y,
|
||||||
|src_pixels, dest_pixels| {
|
|src_pixels, dest_pixels| {
|
||||||
if let Some(blended_pixel) = blend_map.blend(*src_pixels, *dest_pixels) {
|
if let Some(blended_pixel) = blend_map.blend(*src_pixels, *dest_pixels) {
|
||||||
*dest_pixels = blended_pixel;
|
*dest_pixels = blended_pixel;
|
||||||
|
@ -155,7 +162,13 @@ impl IndexedBitmap {
|
||||||
blend_map: Rc<BlendMap>,
|
blend_map: Rc<BlendMap>,
|
||||||
) {
|
) {
|
||||||
per_pixel_flipped_blit(
|
per_pixel_flipped_blit(
|
||||||
self, src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip,
|
self, //
|
||||||
|
src,
|
||||||
|
src_region,
|
||||||
|
dest_x,
|
||||||
|
dest_y,
|
||||||
|
horizontal_flip,
|
||||||
|
vertical_flip,
|
||||||
|src_pixels, dest_pixels| {
|
|src_pixels, dest_pixels| {
|
||||||
if let Some(blended_pixel) = blend_map.blend(*src_pixels, *dest_pixels) {
|
if let Some(blended_pixel) = blend_map.blend(*src_pixels, *dest_pixels) {
|
||||||
*dest_pixels = blended_pixel;
|
*dest_pixels = blended_pixel;
|
||||||
|
@ -175,7 +188,11 @@ impl IndexedBitmap {
|
||||||
offset: u8,
|
offset: u8,
|
||||||
) {
|
) {
|
||||||
per_pixel_blit(
|
per_pixel_blit(
|
||||||
self, src, src_region, dest_x, dest_y,
|
self, //
|
||||||
|
src,
|
||||||
|
src_region,
|
||||||
|
dest_x,
|
||||||
|
dest_y,
|
||||||
|src_pixels, dest_pixels| {
|
|src_pixels, dest_pixels| {
|
||||||
*dest_pixels = (*src_pixels).wrapping_add(offset);
|
*dest_pixels = (*src_pixels).wrapping_add(offset);
|
||||||
},
|
},
|
||||||
|
@ -193,7 +210,13 @@ impl IndexedBitmap {
|
||||||
offset: u8,
|
offset: u8,
|
||||||
) {
|
) {
|
||||||
per_pixel_flipped_blit(
|
per_pixel_flipped_blit(
|
||||||
self, src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip,
|
self, //
|
||||||
|
src,
|
||||||
|
src_region,
|
||||||
|
dest_x,
|
||||||
|
dest_y,
|
||||||
|
horizontal_flip,
|
||||||
|
vertical_flip,
|
||||||
|src_pixels, dest_pixels| {
|
|src_pixels, dest_pixels| {
|
||||||
*dest_pixels = (*src_pixels).wrapping_add(offset);
|
*dest_pixels = (*src_pixels).wrapping_add(offset);
|
||||||
},
|
},
|
||||||
|
@ -210,7 +233,11 @@ impl IndexedBitmap {
|
||||||
blend_map: Rc<BlendMap>,
|
blend_map: Rc<BlendMap>,
|
||||||
) {
|
) {
|
||||||
per_pixel_blit(
|
per_pixel_blit(
|
||||||
self, src, src_region, dest_x, dest_y,
|
self, //
|
||||||
|
src,
|
||||||
|
src_region,
|
||||||
|
dest_x,
|
||||||
|
dest_y,
|
||||||
|src_pixels, dest_pixels| {
|
|src_pixels, dest_pixels| {
|
||||||
if *src_pixels != transparent_color {
|
if *src_pixels != transparent_color {
|
||||||
if let Some(blended_pixel) = blend_map.blend(*src_pixels, *dest_pixels) {
|
if let Some(blended_pixel) = blend_map.blend(*src_pixels, *dest_pixels) {
|
||||||
|
@ -235,7 +262,13 @@ impl IndexedBitmap {
|
||||||
blend_map: Rc<BlendMap>,
|
blend_map: Rc<BlendMap>,
|
||||||
) {
|
) {
|
||||||
per_pixel_flipped_blit(
|
per_pixel_flipped_blit(
|
||||||
self, src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip,
|
self, //
|
||||||
|
src,
|
||||||
|
src_region,
|
||||||
|
dest_x,
|
||||||
|
dest_y,
|
||||||
|
horizontal_flip,
|
||||||
|
vertical_flip,
|
||||||
|src_pixels, dest_pixels| {
|
|src_pixels, dest_pixels| {
|
||||||
if *src_pixels != transparent_color {
|
if *src_pixels != transparent_color {
|
||||||
if let Some(blended_pixel) = blend_map.blend(*src_pixels, *dest_pixels) {
|
if let Some(blended_pixel) = blend_map.blend(*src_pixels, *dest_pixels) {
|
||||||
|
@ -258,7 +291,11 @@ impl IndexedBitmap {
|
||||||
offset: u8,
|
offset: u8,
|
||||||
) {
|
) {
|
||||||
per_pixel_blit(
|
per_pixel_blit(
|
||||||
self, src, src_region, dest_x, dest_y,
|
self, //
|
||||||
|
src,
|
||||||
|
src_region,
|
||||||
|
dest_x,
|
||||||
|
dest_y,
|
||||||
|src_pixels, dest_pixels| {
|
|src_pixels, dest_pixels| {
|
||||||
if *src_pixels != transparent_color {
|
if *src_pixels != transparent_color {
|
||||||
*dest_pixels = (*src_pixels).wrapping_add(offset);
|
*dest_pixels = (*src_pixels).wrapping_add(offset);
|
||||||
|
@ -279,7 +316,13 @@ impl IndexedBitmap {
|
||||||
offset: u8,
|
offset: u8,
|
||||||
) {
|
) {
|
||||||
per_pixel_flipped_blit(
|
per_pixel_flipped_blit(
|
||||||
self, src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip,
|
self, //
|
||||||
|
src,
|
||||||
|
src_region,
|
||||||
|
dest_x,
|
||||||
|
dest_y,
|
||||||
|
horizontal_flip,
|
||||||
|
vertical_flip,
|
||||||
|src_pixels, dest_pixels| {
|
|src_pixels, dest_pixels| {
|
||||||
if *src_pixels != transparent_color {
|
if *src_pixels != transparent_color {
|
||||||
*dest_pixels = (*src_pixels).wrapping_add(offset);
|
*dest_pixels = (*src_pixels).wrapping_add(offset);
|
||||||
|
@ -300,7 +343,14 @@ impl IndexedBitmap {
|
||||||
blend_map: Rc<BlendMap>,
|
blend_map: Rc<BlendMap>,
|
||||||
) {
|
) {
|
||||||
per_pixel_rotozoom_blit(
|
per_pixel_rotozoom_blit(
|
||||||
self, src, src_region, dest_x, dest_y, angle, scale_x, scale_y,
|
self, //
|
||||||
|
src,
|
||||||
|
src_region,
|
||||||
|
dest_x,
|
||||||
|
dest_y,
|
||||||
|
angle,
|
||||||
|
scale_x,
|
||||||
|
scale_y,
|
||||||
|src_pixel, dest_bitmap, draw_x, draw_y| {
|
|src_pixel, dest_bitmap, draw_x, draw_y| {
|
||||||
if let Some(dest_pixel) = dest_bitmap.get_pixel(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) {
|
let draw_pixel = if let Some(blended_pixel) = blend_map.blend(src_pixel, dest_pixel) {
|
||||||
|
@ -327,7 +377,14 @@ impl IndexedBitmap {
|
||||||
blend_map: Rc<BlendMap>,
|
blend_map: Rc<BlendMap>,
|
||||||
) {
|
) {
|
||||||
per_pixel_rotozoom_blit(
|
per_pixel_rotozoom_blit(
|
||||||
self, src, src_region, dest_x, dest_y, angle, scale_x, scale_y,
|
self, //
|
||||||
|
src,
|
||||||
|
src_region,
|
||||||
|
dest_x,
|
||||||
|
dest_y,
|
||||||
|
angle,
|
||||||
|
scale_x,
|
||||||
|
scale_y,
|
||||||
|src_pixel, dest_bitmap, draw_x, draw_y| {
|
|src_pixel, dest_bitmap, draw_x, draw_y| {
|
||||||
if transparent_color != src_pixel {
|
if transparent_color != src_pixel {
|
||||||
if let Some(dest_pixel) = dest_bitmap.get_pixel(draw_x, draw_y) {
|
if let Some(dest_pixel) = dest_bitmap.get_pixel(draw_x, draw_y) {
|
||||||
|
@ -355,7 +412,14 @@ impl IndexedBitmap {
|
||||||
offset: u8,
|
offset: u8,
|
||||||
) {
|
) {
|
||||||
per_pixel_rotozoom_blit(
|
per_pixel_rotozoom_blit(
|
||||||
self, src, src_region, dest_x, dest_y, angle, scale_x, scale_y,
|
self, //
|
||||||
|
src,
|
||||||
|
src_region,
|
||||||
|
dest_x,
|
||||||
|
dest_y,
|
||||||
|
angle,
|
||||||
|
scale_x,
|
||||||
|
scale_y,
|
||||||
|src_pixel, dest_bitmap, draw_x, draw_y| {
|
|src_pixel, dest_bitmap, draw_x, draw_y| {
|
||||||
let src_pixel = src_pixel.wrapping_add(offset);
|
let src_pixel = src_pixel.wrapping_add(offset);
|
||||||
dest_bitmap.set_pixel(draw_x, draw_y, src_pixel);
|
dest_bitmap.set_pixel(draw_x, draw_y, src_pixel);
|
||||||
|
@ -376,7 +440,14 @@ impl IndexedBitmap {
|
||||||
offset: u8,
|
offset: u8,
|
||||||
) {
|
) {
|
||||||
per_pixel_rotozoom_blit(
|
per_pixel_rotozoom_blit(
|
||||||
self, src, src_region, dest_x, dest_y, angle, scale_x, scale_y,
|
self, //
|
||||||
|
src,
|
||||||
|
src_region,
|
||||||
|
dest_x,
|
||||||
|
dest_y,
|
||||||
|
angle,
|
||||||
|
scale_x,
|
||||||
|
scale_y,
|
||||||
|src_pixel, dest_bitmap, draw_x, draw_y| {
|
|src_pixel, dest_bitmap, draw_x, draw_y| {
|
||||||
if transparent_color != src_pixel {
|
if transparent_color != src_pixel {
|
||||||
let src_pixel = src_pixel.wrapping_add(offset);
|
let src_pixel = src_pixel.wrapping_add(offset);
|
||||||
|
@ -413,15 +484,15 @@ impl IndexedBitmap {
|
||||||
RotoZoomTransparentOffset { .. } => {}
|
RotoZoomTransparentOffset { .. } => {}
|
||||||
|
|
||||||
// set axis flip arguments
|
// set axis flip arguments
|
||||||
SolidFlipped { horizontal_flip, vertical_flip, .. } |
|
SolidFlipped { horizontal_flip, vertical_flip, .. }
|
||||||
SolidFlippedBlended { horizontal_flip, vertical_flip, .. } |
|
| SolidFlippedBlended { horizontal_flip, vertical_flip, .. }
|
||||||
SolidFlippedOffset { horizontal_flip, vertical_flip, .. } |
|
| SolidFlippedOffset { horizontal_flip, vertical_flip, .. }
|
||||||
TransparentFlipped { horizontal_flip, vertical_flip, .. } |
|
| TransparentFlipped { horizontal_flip, vertical_flip, .. }
|
||||||
TransparentFlippedBlended { horizontal_flip, vertical_flip, .. } |
|
| TransparentFlippedBlended { horizontal_flip, vertical_flip, .. }
|
||||||
TransparentFlippedSingle { horizontal_flip, vertical_flip, .. } |
|
| TransparentFlippedSingle { horizontal_flip, vertical_flip, .. }
|
||||||
TransparentFlippedOffset { horizontal_flip, vertical_flip, .. } => {
|
| TransparentFlippedOffset { horizontal_flip, vertical_flip, .. } => {
|
||||||
if !clip_blit(
|
if !clip_blit(
|
||||||
self.clip_region(),
|
self.clip_region(), //
|
||||||
&mut src_region,
|
&mut src_region,
|
||||||
&mut dest_x,
|
&mut dest_x,
|
||||||
&mut dest_y,
|
&mut dest_y,
|
||||||
|
@ -435,7 +506,7 @@ impl IndexedBitmap {
|
||||||
// otherwise clip like normal!
|
// otherwise clip like normal!
|
||||||
_ => {
|
_ => {
|
||||||
if !clip_blit(
|
if !clip_blit(
|
||||||
self.clip_region(),
|
self.clip_region(), //
|
||||||
&mut src_region,
|
&mut src_region,
|
||||||
&mut dest_x,
|
&mut dest_x,
|
||||||
&mut dest_y,
|
&mut dest_y,
|
||||||
|
@ -543,9 +614,16 @@ impl IndexedBitmap {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn blit_atlas_unchecked(&mut self, method: IndexedBlitMethod, src: &BitmapAtlas<Self>, index: usize, x: i32, y: i32) {
|
pub unsafe fn blit_atlas_unchecked(
|
||||||
|
&mut self,
|
||||||
|
method: IndexedBlitMethod,
|
||||||
|
src: &BitmapAtlas<Self>,
|
||||||
|
index: usize,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
) {
|
||||||
if let Some(src_region) = src.get(index) {
|
if let Some(src_region) = src.get(index) {
|
||||||
self.blit_region_unchecked(method, src.bitmap(), &src_region, x, y);
|
self.blit_region_unchecked(method, src.bitmap(), &src_region, x, y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use crate::graphics::bitmap::{Bitmap, BitmapError};
|
|
||||||
use crate::graphics::bitmap::rgb::RgbaBitmap;
|
use crate::graphics::bitmap::rgb::RgbaBitmap;
|
||||||
|
use crate::graphics::bitmap::{Bitmap, BitmapError};
|
||||||
use crate::graphics::palette::Palette;
|
use crate::graphics::palette::Palette;
|
||||||
|
|
||||||
pub mod blit;
|
pub mod blit;
|
||||||
|
@ -29,20 +29,14 @@ impl IndexedBitmap {
|
||||||
Some("png") => {
|
Some("png") => {
|
||||||
let (bmp, palette) = Self::load_png_file(path)?;
|
let (bmp, palette) = Self::load_png_file(path)?;
|
||||||
Ok((bmp, palette.expect("Indexed color PNG loaded and should have returned a Palette")))
|
Ok((bmp, palette.expect("Indexed color PNG loaded and should have returned a Palette")))
|
||||||
},
|
}
|
||||||
Some("pcx") => Ok(Self::load_pcx_file(path)?),
|
Some("pcx") => Ok(Self::load_pcx_file(path)?),
|
||||||
Some("gif") => Ok(Self::load_gif_file(path)?),
|
Some("gif") => Ok(Self::load_gif_file(path)?),
|
||||||
Some("iff") | Some("lbm") | Some("pbm") | Some("bbm") => {
|
Some("iff") | Some("lbm") | Some("pbm") | Some("bbm") => Ok(Self::load_iff_file(path)?),
|
||||||
Ok(Self::load_iff_file(path)?)
|
_ => Err(BitmapError::UnknownFileType(String::from("Unrecognized file extension"))),
|
||||||
}
|
|
||||||
_ => Err(BitmapError::UnknownFileType(String::from(
|
|
||||||
"Unrecognized file extension",
|
|
||||||
))),
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(BitmapError::UnknownFileType(String::from(
|
Err(BitmapError::UnknownFileType(String::from("No file extension")))
|
||||||
"No file extension",
|
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,4 +67,4 @@ impl IndexedBitmap {
|
||||||
self.copy_as_argb_to(output.pixels_mut(), palette);
|
self.copy_as_argb_to(output.pixels_mut(), palette);
|
||||||
output
|
output
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,14 +8,15 @@ impl IndexedBitmap {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_blended_pixel(&mut self, x: i32, y: i32, color: u8, blend_map: &BlendMap) {
|
pub fn set_blended_pixel(&mut self, x: i32, y: i32, color: u8, blend_map: &BlendMap) {
|
||||||
self.set_custom_pixel(
|
self.set_custom_pixel(
|
||||||
x, y,
|
x, //
|
||||||
|
y,
|
||||||
|dest_color| {
|
|dest_color| {
|
||||||
if let Some(blended_color) = blend_map.blend(color, dest_color) {
|
if let Some(blended_color) = blend_map.blend(color, dest_color) {
|
||||||
blended_color
|
blended_color
|
||||||
} else {
|
} else {
|
||||||
color
|
color
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,14 +27,15 @@ impl IndexedBitmap {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn set_blended_pixel_unchecked(&mut self, x: i32, y: i32, color: u8, blend_map: &BlendMap) {
|
pub unsafe fn set_blended_pixel_unchecked(&mut self, x: i32, y: i32, color: u8, blend_map: &BlendMap) {
|
||||||
self.set_custom_pixel_unchecked(
|
self.set_custom_pixel_unchecked(
|
||||||
x, y,
|
x, //
|
||||||
|
y,
|
||||||
|dest_color| {
|
|dest_color| {
|
||||||
if let Some(blended_color) = blend_map.blend(color, dest_color) {
|
if let Some(blended_color) = blend_map.blend(color, dest_color) {
|
||||||
blended_color
|
blended_color
|
||||||
} else {
|
} else {
|
||||||
color
|
color
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,10 +44,11 @@ impl IndexedBitmap {
|
||||||
pub fn blended_line(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: u8, blend_map: &BlendMap) {
|
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) {
|
if let Some(blend_mapping) = blend_map.get_mapping(color) {
|
||||||
self.line_custom(
|
self.line_custom(
|
||||||
x1, y1, x2, y2,
|
x1, //
|
||||||
|dest_color| {
|
y1,
|
||||||
blend_mapping[dest_color as usize]
|
x2,
|
||||||
}
|
y2,
|
||||||
|
|dest_color| blend_mapping[dest_color as usize],
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
self.line(x1, y1, x2, y2, color);
|
self.line(x1, y1, x2, y2, color);
|
||||||
|
@ -57,10 +60,10 @@ impl IndexedBitmap {
|
||||||
pub fn blended_horiz_line(&mut self, x1: i32, x2: i32, y: i32, color: u8, blend_map: &BlendMap) {
|
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) {
|
if let Some(blend_mapping) = blend_map.get_mapping(color) {
|
||||||
self.horiz_line_custom(
|
self.horiz_line_custom(
|
||||||
x1, x2, y,
|
x1, //
|
||||||
|dest_color| {
|
x2,
|
||||||
blend_mapping[dest_color as usize]
|
y,
|
||||||
}
|
|dest_color| blend_mapping[dest_color as usize],
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
self.horiz_line(x1, x2, y, color);
|
self.horiz_line(x1, x2, y, color);
|
||||||
|
@ -72,10 +75,10 @@ impl IndexedBitmap {
|
||||||
pub fn blended_vert_line(&mut self, x: i32, y1: i32, y2: i32, color: u8, blend_map: &BlendMap) {
|
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) {
|
if let Some(blend_mapping) = blend_map.get_mapping(color) {
|
||||||
self.vert_line_custom(
|
self.vert_line_custom(
|
||||||
x, y1, y2,
|
x, //
|
||||||
|dest_color| {
|
y1,
|
||||||
blend_mapping[dest_color as usize]
|
y2,
|
||||||
}
|
|dest_color| blend_mapping[dest_color as usize],
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
self.vert_line(x, y1, y2, color);
|
self.vert_line(x, y1, y2, color);
|
||||||
|
@ -89,10 +92,11 @@ impl IndexedBitmap {
|
||||||
pub fn blended_rect(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: u8, blend_map: &BlendMap) {
|
pub fn blended_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) {
|
if let Some(blend_mapping) = blend_map.get_mapping(color) {
|
||||||
self.rect_custom(
|
self.rect_custom(
|
||||||
x1, y1, x2, y2,
|
x1, //
|
||||||
|dest_color| {
|
y1,
|
||||||
blend_mapping[dest_color as usize]
|
x2,
|
||||||
}
|
y2,
|
||||||
|
|dest_color| blend_mapping[dest_color as usize],
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
self.rect(x1, y1, x2, y2, color);
|
self.rect(x1, y1, x2, y2, color);
|
||||||
|
@ -106,13 +110,14 @@ impl IndexedBitmap {
|
||||||
pub fn blended_filled_rect(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: u8, blend_map: &BlendMap) {
|
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) {
|
if let Some(blend_mapping) = blend_map.get_mapping(color) {
|
||||||
self.filled_rect_custom(
|
self.filled_rect_custom(
|
||||||
x1, y1, x2, y2,
|
x1, //
|
||||||
|dest_color| {
|
y1,
|
||||||
blend_mapping[dest_color as usize]
|
x2,
|
||||||
}
|
y2,
|
||||||
|
|dest_color| blend_mapping[dest_color as usize],
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
self.filled_rect(x1, y1, x2, y2, color);
|
self.filled_rect(x1, y1, x2, y2, color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@ pub struct Bitmap<PixelType: Pixel> {
|
||||||
|
|
||||||
impl<PixelType: Pixel> std::fmt::Debug for Bitmap<PixelType> {
|
impl<PixelType: Pixel> std::fmt::Debug for Bitmap<PixelType> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
f.debug_struct("Bitmap")
|
f.debug_struct("Bitmap") //
|
||||||
.field("width", &self.width)
|
.field("width", &self.width)
|
||||||
.field("height", &self.height)
|
.field("height", &self.height)
|
||||||
.field("clip_region", &self.clip_region)
|
.field("clip_region", &self.clip_region)
|
||||||
|
@ -72,15 +72,10 @@ impl<PixelType: Pixel> Bitmap<PixelType> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Bitmap {
|
Ok(Bitmap {
|
||||||
width,
|
width, //
|
||||||
height,
|
height,
|
||||||
pixels: vec![color; (width * height) as usize].into_boxed_slice(),
|
pixels: vec![color; (width * height) as usize].into_boxed_slice(),
|
||||||
clip_region: Rect {
|
clip_region: Rect { x: 0, y: 0, width, height },
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,7 +133,7 @@ impl<PixelType: Pixel> Bitmap<PixelType> {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn full_bounds(&self) -> Rect {
|
pub fn full_bounds(&self) -> Rect {
|
||||||
Rect {
|
Rect {
|
||||||
x: 0,
|
x: 0, //
|
||||||
y: 0,
|
y: 0,
|
||||||
width: self.width,
|
width: self.width,
|
||||||
height: self.height,
|
height: self.height,
|
||||||
|
@ -215,7 +210,7 @@ impl<PixelType: Pixel> Bitmap<PixelType> {
|
||||||
pub unsafe fn pixels_at_mut_unchecked(&mut self, x: i32, y: i32) -> &mut [PixelType] {
|
pub unsafe fn pixels_at_mut_unchecked(&mut self, x: i32, y: i32) -> &mut [PixelType] {
|
||||||
let offset = self.get_offset_to_xy(x, y);
|
let offset = self.get_offset_to_xy(x, y);
|
||||||
std::slice::from_raw_parts_mut(
|
std::slice::from_raw_parts_mut(
|
||||||
self.pixels.as_mut_ptr().add(offset),
|
self.pixels.as_mut_ptr().add(offset), //
|
||||||
self.pixels.len() - offset,
|
self.pixels.len() - offset,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -318,24 +313,8 @@ pub mod tests {
|
||||||
assert_eq!(32, bmp.height());
|
assert_eq!(32, bmp.height());
|
||||||
assert_eq!(15, bmp.right());
|
assert_eq!(15, bmp.right());
|
||||||
assert_eq!(31, bmp.bottom());
|
assert_eq!(31, bmp.bottom());
|
||||||
assert_eq!(
|
assert_eq!(Rect { x: 0, y: 0, width: 16, height: 32 }, bmp.full_bounds());
|
||||||
Rect {
|
assert_eq!(Rect { x: 0, y: 0, width: 16, height: 32 }, *bmp.clip_region());
|
||||||
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]
|
#[test]
|
||||||
|
@ -343,10 +322,7 @@ pub mod tests {
|
||||||
let mut bmp = Bitmap::<u8>::new(8, 8).unwrap();
|
let mut bmp = Bitmap::<u8>::new(8, 8).unwrap();
|
||||||
bmp.pixels_mut().copy_from_slice(RAW_BMP_PIXELS);
|
bmp.pixels_mut().copy_from_slice(RAW_BMP_PIXELS);
|
||||||
|
|
||||||
assert_matches!(
|
assert_matches!(Bitmap::<u8>::from(&bmp, &Rect::new(0, 0, 16, 16)), Err(BitmapError::OutOfBounds));
|
||||||
Bitmap::<u8>::from(&bmp, &Rect::new(0, 0, 16, 16)),
|
|
||||||
Err(BitmapError::OutOfBounds)
|
|
||||||
);
|
|
||||||
|
|
||||||
let copy = Bitmap::<u8>::from(&bmp, &Rect::new(0, 0, 8, 8)).unwrap();
|
let copy = Bitmap::<u8>::from(&bmp, &Rect::new(0, 0, 8, 8)).unwrap();
|
||||||
assert_eq!(bmp.pixels(), copy.pixels());
|
assert_eq!(bmp.pixels(), copy.pixels());
|
||||||
|
@ -382,15 +358,7 @@ pub mod tests {
|
||||||
|
|
||||||
let new_clip_region = Rect::from_coords(4, 2, 12, 6);
|
let new_clip_region = Rect::from_coords(4, 2, 12, 6);
|
||||||
bmp.set_clip_region(&new_clip_region);
|
bmp.set_clip_region(&new_clip_region);
|
||||||
assert_eq!(
|
assert_eq!(Rect { x: 0, y: 0, width: 16, height: 8 }, bmp.full_bounds());
|
||||||
Rect {
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
width: 16,
|
|
||||||
height: 8,
|
|
||||||
},
|
|
||||||
bmp.full_bounds()
|
|
||||||
);
|
|
||||||
assert_eq!(new_clip_region, *bmp.clip_region());
|
assert_eq!(new_clip_region, *bmp.clip_region());
|
||||||
assert!(bmp.is_xy_visible(4, 2));
|
assert!(bmp.is_xy_visible(4, 2));
|
||||||
assert!(bmp.is_xy_visible(12, 2));
|
assert!(bmp.is_xy_visible(12, 2));
|
||||||
|
|
|
@ -93,11 +93,7 @@ impl PcxHeader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_pcx_data<T: WriteBytesExt>(
|
fn write_pcx_data<T: WriteBytesExt>(writer: &mut T, run_count: u8, pixel: u8) -> Result<(), PcxError> {
|
||||||
writer: &mut T,
|
|
||||||
run_count: u8,
|
|
||||||
pixel: u8,
|
|
||||||
) -> Result<(), PcxError> {
|
|
||||||
if (run_count > 1) || ((pixel & 0xc0) == 0xc0) {
|
if (run_count > 1) || ((pixel & 0xc0) == 0xc0) {
|
||||||
writer.write_u8(0xc0 | run_count)?;
|
writer.write_u8(0xc0 | run_count)?;
|
||||||
}
|
}
|
||||||
|
@ -106,9 +102,7 @@ fn write_pcx_data<T: WriteBytesExt>(
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IndexedBitmap {
|
impl IndexedBitmap {
|
||||||
pub fn load_pcx_bytes<T: ReadBytesExt + Seek>(
|
pub fn load_pcx_bytes<T: ReadBytesExt + Seek>(reader: &mut T) -> Result<(IndexedBitmap, Palette), PcxError> {
|
||||||
reader: &mut T,
|
|
||||||
) -> Result<(IndexedBitmap, Palette), PcxError> {
|
|
||||||
let header = PcxHeader::read(reader)?;
|
let header = PcxHeader::read(reader)?;
|
||||||
|
|
||||||
if header.manufacturer != 10 {
|
if header.manufacturer != 10 {
|
||||||
|
@ -117,14 +111,10 @@ impl IndexedBitmap {
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
if header.version != 5 {
|
if header.version != 5 {
|
||||||
return Err(PcxError::BadFile(String::from(
|
return Err(PcxError::BadFile(String::from("Only version 5 PCX files are supported")));
|
||||||
"Only version 5 PCX files are supported",
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
if header.encoding != 1 {
|
if header.encoding != 1 {
|
||||||
return Err(PcxError::BadFile(String::from(
|
return Err(PcxError::BadFile(String::from("Only RLE-compressed PCX files are supported")));
|
||||||
"Only RLE-compressed PCX files are supported",
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
if header.bpp != 8 {
|
if header.bpp != 8 {
|
||||||
return Err(PcxError::BadFile(String::from(
|
return Err(PcxError::BadFile(String::from(
|
||||||
|
@ -132,9 +122,7 @@ impl IndexedBitmap {
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
if header.x2 == 0 || header.y2 == 0 {
|
if header.x2 == 0 || header.y2 == 0 {
|
||||||
return Err(PcxError::BadFile(String::from(
|
return Err(PcxError::BadFile(String::from("Invalid PCX image dimensions")));
|
||||||
"Invalid PCX image dimensions",
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// read the PCX file's pixel data into a bitmap
|
// read the PCX file's pixel data into a bitmap
|
||||||
|
@ -185,9 +173,7 @@ impl IndexedBitmap {
|
||||||
|
|
||||||
let palette_marker = reader.read_u8()?;
|
let palette_marker = reader.read_u8()?;
|
||||||
if palette_marker != 0x0c {
|
if palette_marker != 0x0c {
|
||||||
return Err(PcxError::BadFile(String::from(
|
return Err(PcxError::BadFile(String::from("Palette not found at end of file")));
|
||||||
"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)?;
|
||||||
|
@ -201,11 +187,7 @@ impl IndexedBitmap {
|
||||||
Self::load_pcx_bytes(&mut reader)
|
Self::load_pcx_bytes(&mut reader)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_pcx_bytes<T: WriteBytesExt>(
|
pub fn to_pcx_bytes<T: WriteBytesExt>(&self, writer: &mut T, palette: &Palette) -> Result<(), PcxError> {
|
||||||
&self,
|
|
||||||
writer: &mut T,
|
|
||||||
palette: &Palette,
|
|
||||||
) -> Result<(), PcxError> {
|
|
||||||
let header = PcxHeader {
|
let header = PcxHeader {
|
||||||
manufacturer: 10,
|
manufacturer: 10,
|
||||||
version: 5,
|
version: 5,
|
||||||
|
@ -285,9 +267,7 @@ impl IndexedBitmap {
|
||||||
// multi-pixel-depth support.
|
// multi-pixel-depth support.
|
||||||
|
|
||||||
impl RgbaBitmap {
|
impl RgbaBitmap {
|
||||||
pub fn load_pcx_bytes<T: ReadBytesExt + Seek>(
|
pub fn load_pcx_bytes<T: ReadBytesExt + Seek>(reader: &mut T) -> Result<(RgbaBitmap, Palette), PcxError> {
|
||||||
reader: &mut T,
|
|
||||||
) -> Result<(RgbaBitmap, Palette), PcxError> {
|
|
||||||
let (temp_bitmap, palette) = IndexedBitmap::load_pcx_bytes(reader)?;
|
let (temp_bitmap, palette) = IndexedBitmap::load_pcx_bytes(reader)?;
|
||||||
let output = temp_bitmap.to_rgba(&palette);
|
let output = temp_bitmap.to_rgba(&palette);
|
||||||
Ok((output, palette))
|
Ok((output, palette))
|
||||||
|
@ -322,9 +302,10 @@ pub mod tests {
|
||||||
|
|
||||||
let ref_pixels = load_raw_indexed(test_file(Path::new("small.bin")).as_path())?;
|
let ref_pixels = load_raw_indexed(test_file(Path::new("small.bin")).as_path())?;
|
||||||
let dp2_palette = Palette::load_from_file(
|
let dp2_palette = Palette::load_from_file(
|
||||||
test_assets_file(Path::new("dp2.pal")).as_path(),
|
test_assets_file(Path::new("dp2.pal")).as_path(), //
|
||||||
PaletteFormat::Normal
|
PaletteFormat::Normal,
|
||||||
).unwrap();
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let (bmp, palette) = IndexedBitmap::load_pcx_file(test_file(Path::new("small.pcx")).as_path())?;
|
let (bmp, palette) = IndexedBitmap::load_pcx_file(test_file(Path::new("small.pcx")).as_path())?;
|
||||||
assert_eq!(16, bmp.width());
|
assert_eq!(16, bmp.width());
|
||||||
|
|
|
@ -7,12 +7,12 @@ use std::path::Path;
|
||||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::graphics::bitmap::Bitmap;
|
|
||||||
use crate::graphics::bitmap::indexed::IndexedBitmap;
|
use crate::graphics::bitmap::indexed::IndexedBitmap;
|
||||||
use crate::graphics::bitmap::rgb::RgbaBitmap;
|
use crate::graphics::bitmap::rgb::RgbaBitmap;
|
||||||
use crate::graphics::palette::Palette;
|
use crate::graphics::bitmap::Bitmap;
|
||||||
|
use crate::graphics::color::{from_argb32, from_rgb32, to_argb32, to_rgb32};
|
||||||
|
use crate::graphics::palette::{Palette, PaletteError, PaletteFormat};
|
||||||
use crate::graphics::Pixel;
|
use crate::graphics::Pixel;
|
||||||
use crate::prelude::{from_argb32, from_rgb32, PaletteError, PaletteFormat, to_argb32, to_rgb32};
|
|
||||||
use crate::utils::bytes::ReadFixedLengthByteArray;
|
use crate::utils::bytes::ReadFixedLengthByteArray;
|
||||||
|
|
||||||
const PNG_HEADER: [u8; 8] = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];
|
const PNG_HEADER: [u8; 8] = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];
|
||||||
|
@ -76,7 +76,7 @@ struct ChunkHeader {
|
||||||
impl ChunkHeader {
|
impl ChunkHeader {
|
||||||
pub fn read<T: ReadBytesExt>(reader: &mut T) -> Result<Self, PngError> {
|
pub fn read<T: ReadBytesExt>(reader: &mut T) -> Result<Self, PngError> {
|
||||||
Ok(ChunkHeader {
|
Ok(ChunkHeader {
|
||||||
size: reader.read_u32::<BigEndian>()?,
|
size: reader.read_u32::<BigEndian>()?, //
|
||||||
name: reader.read_bytes()?,
|
name: reader.read_bytes()?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -102,7 +102,7 @@ struct ImageHeaderChunk {
|
||||||
impl ImageHeaderChunk {
|
impl ImageHeaderChunk {
|
||||||
pub fn read<T: ReadBytesExt>(reader: &mut T) -> Result<Self, PngError> {
|
pub fn read<T: ReadBytesExt>(reader: &mut T) -> Result<Self, PngError> {
|
||||||
Ok(ImageHeaderChunk {
|
Ok(ImageHeaderChunk {
|
||||||
width: reader.read_u32::<BigEndian>()?,
|
width: reader.read_u32::<BigEndian>()?, //
|
||||||
height: reader.read_u32::<BigEndian>()?,
|
height: reader.read_u32::<BigEndian>()?,
|
||||||
bpp: reader.read_u8()?,
|
bpp: reader.read_u8()?,
|
||||||
format: ColorFormat::from(reader.read_u8()?)?,
|
format: ColorFormat::from(reader.read_u8()?)?,
|
||||||
|
@ -234,7 +234,7 @@ impl ScanlineBuffer {
|
||||||
// unsigned arithmetic modulo 256 *except* for the average calculation itself which must not overflow!
|
// unsigned arithmetic modulo 256 *except* for the average calculation itself which must not overflow!
|
||||||
let average = (a + b) / 2;
|
let average = (a + b) / 2;
|
||||||
byte.wrapping_add(average as u8)
|
byte.wrapping_add(average as u8)
|
||||||
},
|
}
|
||||||
Filter::Paeth => {
|
Filter::Paeth => {
|
||||||
let a = if x < self.bpp { 0 } else { self.current[x - self.bpp] } as i16;
|
let a = if x < self.bpp { 0 } else { self.current[x - self.bpp] } as i16;
|
||||||
let b = if y < 1 { 0 } else { self.previous[x] } as i16;
|
let b = if y < 1 { 0 } else { self.previous[x] } as i16;
|
||||||
|
@ -252,7 +252,7 @@ impl ScanlineBuffer {
|
||||||
};
|
};
|
||||||
// all of the above must not overflow. however, this last part is unsigned arithmetic modulo 256
|
// all of the above must not overflow. however, this last part is unsigned arithmetic modulo 256
|
||||||
byte.wrapping_add(value)
|
byte.wrapping_add(value)
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -307,10 +307,13 @@ impl ScanlinePixelConverter<u8> for ScanlineBuffer {
|
||||||
fn read_pixel(&mut self, x: usize, _palette: &Option<Palette>) -> Result<u8, PngError> {
|
fn read_pixel(&mut self, x: usize, _palette: &Option<Palette>) -> Result<u8, PngError> {
|
||||||
let offset = x * self.bpp;
|
let offset = x * self.bpp;
|
||||||
match self.format {
|
match self.format {
|
||||||
ColorFormat::IndexedColor => {
|
ColorFormat::IndexedColor => Ok(self.current[offset]),
|
||||||
Ok(self.current[offset])
|
_ => {
|
||||||
},
|
return Err(PngError::BadFile(format!(
|
||||||
_ => return Err(PngError::BadFile(format!("Unsupported color format for this PixelReader: {:?}", self.format))),
|
"Unsupported color format for this PixelReader: {:?}",
|
||||||
|
self.format
|
||||||
|
)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -320,8 +323,13 @@ impl ScanlinePixelConverter<u8> for ScanlineBuffer {
|
||||||
ColorFormat::IndexedColor => {
|
ColorFormat::IndexedColor => {
|
||||||
self.current[offset] = pixel;
|
self.current[offset] = pixel;
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
}
|
||||||
_ => return Err(PngError::BadFile(format!("Unsupported color format for this PixelReader: {:?}", self.format))),
|
_ => {
|
||||||
|
return Err(PngError::BadFile(format!(
|
||||||
|
"Unsupported color format for this PixelReader: {:?}",
|
||||||
|
self.format
|
||||||
|
)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -335,23 +343,30 @@ impl ScanlinePixelConverter<u32> for ScanlineBuffer {
|
||||||
if let Some(palette) = palette {
|
if let Some(palette) = palette {
|
||||||
Ok(palette[color])
|
Ok(palette[color])
|
||||||
} else {
|
} else {
|
||||||
return Err(PngError::BadFile(String::from("No palette to map indexed-color format pixels to RGBA format destination")));
|
return Err(PngError::BadFile(String::from(
|
||||||
|
"No palette to map indexed-color format pixels to RGBA format destination",
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
ColorFormat::RGB => {
|
ColorFormat::RGB => {
|
||||||
let r = self.current[offset];
|
let r = self.current[offset];
|
||||||
let g = self.current[offset + 1];
|
let g = self.current[offset + 1];
|
||||||
let b = self.current[offset + 2];
|
let b = self.current[offset + 2];
|
||||||
Ok(to_rgb32(r, g, b))
|
Ok(to_rgb32(r, g, b))
|
||||||
},
|
}
|
||||||
ColorFormat::RGBA => {
|
ColorFormat::RGBA => {
|
||||||
let r = self.current[offset];
|
let r = self.current[offset];
|
||||||
let g = self.current[offset + 1];
|
let g = self.current[offset + 1];
|
||||||
let b = self.current[offset + 2];
|
let b = self.current[offset + 2];
|
||||||
let a = self.current[offset + 3];
|
let a = self.current[offset + 3];
|
||||||
Ok(to_argb32(a, r, g, b))
|
Ok(to_argb32(a, r, g, b))
|
||||||
},
|
}
|
||||||
_ => return Err(PngError::BadFile(format!("Unsupported color format for this PixelReader: {:?}", self.format))),
|
_ => {
|
||||||
|
return Err(PngError::BadFile(format!(
|
||||||
|
"Unsupported color format for this PixelReader: {:?}",
|
||||||
|
self.format
|
||||||
|
)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -364,7 +379,7 @@ impl ScanlinePixelConverter<u32> for ScanlineBuffer {
|
||||||
self.current[offset + 1] = g;
|
self.current[offset + 1] = g;
|
||||||
self.current[offset + 2] = b;
|
self.current[offset + 2] = b;
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
}
|
||||||
ColorFormat::RGBA => {
|
ColorFormat::RGBA => {
|
||||||
let (a, r, g, b) = from_argb32(pixel);
|
let (a, r, g, b) = from_argb32(pixel);
|
||||||
self.current[offset] = r;
|
self.current[offset] = r;
|
||||||
|
@ -372,20 +387,22 @@ impl ScanlinePixelConverter<u32> for ScanlineBuffer {
|
||||||
self.current[offset + 2] = b;
|
self.current[offset + 2] = b;
|
||||||
self.current[offset + 3] = a;
|
self.current[offset + 3] = a;
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
}
|
||||||
_ => return Err(PngError::BadFile(format!("Unsupported color format for this PixelReader: {:?}", self.format))),
|
_ => {
|
||||||
|
return Err(PngError::BadFile(format!(
|
||||||
|
"Unsupported color format for this PixelReader: {:?}",
|
||||||
|
self.format
|
||||||
|
)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn load_png_bytes<Reader, PixelType>(reader: &mut Reader) -> Result<(Bitmap<PixelType>, Option<Palette>), PngError>
|
||||||
fn load_png_bytes<Reader, PixelType>(
|
|
||||||
reader: &mut Reader
|
|
||||||
) -> Result<(Bitmap<PixelType>, Option<Palette>), PngError>
|
|
||||||
where
|
where
|
||||||
Reader: ReadBytesExt,
|
Reader: ReadBytesExt,
|
||||||
PixelType: Pixel,
|
PixelType: Pixel,
|
||||||
ScanlineBuffer: ScanlinePixelConverter<PixelType>
|
ScanlineBuffer: ScanlinePixelConverter<PixelType>,
|
||||||
{
|
{
|
||||||
let header: [u8; 8] = reader.read_bytes()?;
|
let header: [u8; 8] = reader.read_bytes()?;
|
||||||
if header != PNG_HEADER {
|
if header != PNG_HEADER {
|
||||||
|
@ -409,9 +426,10 @@ where
|
||||||
if ihdr.bpp != 8 {
|
if ihdr.bpp != 8 {
|
||||||
return Err(PngError::BadFile(String::from("Unsupported color bit depth.")));
|
return Err(PngError::BadFile(String::from("Unsupported color bit depth.")));
|
||||||
}
|
}
|
||||||
if ihdr.format != ColorFormat::IndexedColor
|
if ihdr.format != ColorFormat::IndexedColor // .
|
||||||
&& ihdr.format != ColorFormat::RGB
|
&& ihdr.format != ColorFormat::RGB
|
||||||
&& ihdr.format != ColorFormat::RGBA {
|
&& ihdr.format != ColorFormat::RGBA
|
||||||
|
{
|
||||||
return Err(PngError::BadFile(String::from("Unsupported pixel color format.")));
|
return Err(PngError::BadFile(String::from("Unsupported pixel color format.")));
|
||||||
}
|
}
|
||||||
if ihdr.compression != 0 {
|
if ihdr.compression != 0 {
|
||||||
|
@ -438,7 +456,7 @@ where
|
||||||
let chunk_bytes = read_chunk_data(reader, &chunk_header)?;
|
let chunk_bytes = read_chunk_data(reader, &chunk_header)?;
|
||||||
let num_colors = (chunk_header.size / 3) as usize;
|
let num_colors = (chunk_header.size / 3) as usize;
|
||||||
Some(Palette::load_num_colors_from_bytes(
|
Some(Palette::load_num_colors_from_bytes(
|
||||||
&mut chunk_bytes.as_slice(),
|
&mut chunk_bytes.as_slice(), //
|
||||||
PaletteFormat::Normal,
|
PaletteFormat::Normal,
|
||||||
num_colors,
|
num_colors,
|
||||||
)?)
|
)?)
|
||||||
|
@ -473,7 +491,9 @@ where
|
||||||
scanline_buffer.read_line(&mut deflater)?;
|
scanline_buffer.read_line(&mut deflater)?;
|
||||||
for x in 0..ihdr.width as usize {
|
for x in 0..ihdr.width as usize {
|
||||||
let pixel = scanline_buffer.read_pixel(x, &palette)?;
|
let pixel = scanline_buffer.read_pixel(x, &palette)?;
|
||||||
unsafe { output.set_pixel_unchecked(x as i32, y as i32, pixel); }
|
unsafe {
|
||||||
|
output.set_pixel_unchecked(x as i32, y as i32, pixel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -563,9 +583,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IndexedBitmap {
|
impl IndexedBitmap {
|
||||||
pub fn load_png_bytes<T: ReadBytesExt>(
|
pub fn load_png_bytes<T: ReadBytesExt>(reader: &mut T) -> Result<(IndexedBitmap, Option<Palette>), PngError> {
|
||||||
reader: &mut T,
|
|
||||||
) -> Result<(IndexedBitmap, Option<Palette>), PngError> {
|
|
||||||
load_png_bytes(reader)
|
load_png_bytes(reader)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -575,11 +593,7 @@ impl IndexedBitmap {
|
||||||
Self::load_png_bytes(&mut reader)
|
Self::load_png_bytes(&mut reader)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_png_bytes<T: WriteBytesExt>(
|
pub fn to_png_bytes<T: WriteBytesExt>(&self, writer: &mut T, palette: &Palette) -> Result<(), PngError> {
|
||||||
&self,
|
|
||||||
writer: &mut T,
|
|
||||||
palette: &Palette,
|
|
||||||
) -> Result<(), PngError> {
|
|
||||||
write_png_bytes(writer, &self, ColorFormat::IndexedColor, Some(palette))
|
write_png_bytes(writer, &self, ColorFormat::IndexedColor, Some(palette))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -591,9 +605,7 @@ impl IndexedBitmap {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RgbaBitmap {
|
impl RgbaBitmap {
|
||||||
pub fn load_png_bytes<T: ReadBytesExt>(
|
pub fn load_png_bytes<T: ReadBytesExt>(reader: &mut T) -> Result<(RgbaBitmap, Option<Palette>), PngError> {
|
||||||
reader: &mut T,
|
|
||||||
) -> Result<(RgbaBitmap, Option<Palette>), PngError> {
|
|
||||||
load_png_bytes(reader)
|
load_png_bytes(reader)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -603,11 +615,7 @@ impl RgbaBitmap {
|
||||||
Self::load_png_bytes(&mut reader)
|
Self::load_png_bytes(&mut reader)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_png_bytes<T: WriteBytesExt>(
|
pub fn to_png_bytes<T: WriteBytesExt>(&self, writer: &mut T, format: PngFormat) -> Result<(), PngError> {
|
||||||
&self,
|
|
||||||
writer: &mut T,
|
|
||||||
format: PngFormat,
|
|
||||||
) -> Result<(), PngError> {
|
|
||||||
write_png_bytes(
|
write_png_bytes(
|
||||||
writer,
|
writer,
|
||||||
&self,
|
&self,
|
||||||
|
@ -619,10 +627,7 @@ impl RgbaBitmap {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_png_file(
|
pub fn to_png_file(&self, path: &Path, format: PngFormat) -> Result<(), PngError> {
|
||||||
&self, path: &Path,
|
|
||||||
format: PngFormat,
|
|
||||||
) -> Result<(), PngError> {
|
|
||||||
let f = File::create(path)?;
|
let f = File::create(path)?;
|
||||||
let mut writer = BufWriter::new(f);
|
let mut writer = BufWriter::new(f);
|
||||||
self.to_png_bytes(&mut writer, format)
|
self.to_png_bytes(&mut writer, format)
|
||||||
|
@ -946,4 +951,4 @@ pub mod tests {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,7 +73,7 @@ impl<PixelType: Pixel> Bitmap<PixelType> {
|
||||||
/// Renders a single character using the font given.
|
/// Renders a single character using the font given.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn print_char<T: Font>(&mut self, ch: char, x: i32, y: i32, opts: FontRenderOpts<PixelType>, font: &T) {
|
pub fn print_char<T: Font>(&mut self, ch: char, x: i32, y: i32, opts: FontRenderOpts<PixelType>, font: &T) {
|
||||||
font.character(ch)
|
font.character(ch) //
|
||||||
.draw(self, x, y, opts);
|
.draw(self, x, y, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,7 +287,7 @@ impl<PixelType: Pixel> Bitmap<PixelType> {
|
||||||
swap(&mut y1, &mut y2);
|
swap(&mut y1, &mut y2);
|
||||||
}
|
}
|
||||||
let mut region = Rect {
|
let mut region = Rect {
|
||||||
x: x1,
|
x: x1, //
|
||||||
y: y1,
|
y: y1,
|
||||||
width: (x2 - x1 + 1) as u32,
|
width: (x2 - x1 + 1) as u32,
|
||||||
height: (y2 - y1 + 1) as u32,
|
height: (y2 - y1 + 1) as u32,
|
||||||
|
@ -345,7 +345,7 @@ impl<PixelType: Pixel> Bitmap<PixelType> {
|
||||||
mut y1: i32,
|
mut y1: i32,
|
||||||
mut x2: i32,
|
mut x2: i32,
|
||||||
mut y2: i32,
|
mut y2: i32,
|
||||||
pixel_fn: impl Fn(PixelType) -> PixelType
|
pixel_fn: impl Fn(PixelType) -> PixelType,
|
||||||
) {
|
) {
|
||||||
// note: need to manually do all this instead of just relying on Rect::from_coords (which
|
// 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
|
// could otherwise figure all this out for us) mainly just because we need the post-swap
|
||||||
|
@ -357,7 +357,7 @@ impl<PixelType: Pixel> Bitmap<PixelType> {
|
||||||
swap(&mut y1, &mut y2);
|
swap(&mut y1, &mut y2);
|
||||||
}
|
}
|
||||||
let mut region = Rect {
|
let mut region = Rect {
|
||||||
x: x1,
|
x: x1, //
|
||||||
y: y1,
|
y: y1,
|
||||||
width: (x2 - x1 + 1) as u32,
|
width: (x2 - x1 + 1) as u32,
|
||||||
height: (y2 - y1 + 1) as u32,
|
height: (y2 - y1 + 1) as u32,
|
||||||
|
@ -394,7 +394,8 @@ impl<PixelType: Pixel> Bitmap<PixelType> {
|
||||||
// bottom line, only if y2 was originally within bounds
|
// bottom line, only if y2 was originally within bounds
|
||||||
if y2 == region.bottom() {
|
if y2 == region.bottom() {
|
||||||
unsafe {
|
unsafe {
|
||||||
let dest = &mut self.pixels_at_mut_unchecked(horiz_draw_x, region.bottom())[0..horiz_draw_width as usize];
|
let dest =
|
||||||
|
&mut self.pixels_at_mut_unchecked(horiz_draw_x, region.bottom())[0..horiz_draw_width as usize];
|
||||||
for pixel in dest.iter_mut() {
|
for pixel in dest.iter_mut() {
|
||||||
*pixel = pixel_fn(*pixel);
|
*pixel = pixel_fn(*pixel);
|
||||||
}
|
}
|
||||||
|
@ -450,7 +451,7 @@ impl<PixelType: Pixel> Bitmap<PixelType> {
|
||||||
y1: i32,
|
y1: i32,
|
||||||
x2: i32,
|
x2: i32,
|
||||||
y2: i32,
|
y2: i32,
|
||||||
pixel_fn: impl Fn(PixelType) -> PixelType
|
pixel_fn: impl Fn(PixelType) -> PixelType,
|
||||||
) {
|
) {
|
||||||
let mut region = Rect::from_coords(x1, y1, x2, y2);
|
let mut region = Rect::from_coords(x1, y1, x2, y2);
|
||||||
if region.clamp_to(&self.clip_region) {
|
if region.clamp_to(&self.clip_region) {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::graphics::bitmap::blit::{clip_blit, per_pixel_blit, per_pixel_flipped_blit, per_pixel_rotozoom_blit};
|
use crate::graphics::bitmap::blit::{clip_blit, per_pixel_blit, per_pixel_flipped_blit, per_pixel_rotozoom_blit};
|
||||||
use crate::graphics::bitmap::rgb::RgbaBitmap;
|
use crate::graphics::bitmap::rgb::RgbaBitmap;
|
||||||
use crate::graphics::bitmapatlas::BitmapAtlas;
|
use crate::graphics::bitmapatlas::BitmapAtlas;
|
||||||
use crate::graphics::color::{BlendFunction, tint_argb32};
|
use crate::graphics::color::{tint_argb32, BlendFunction};
|
||||||
use crate::math::rect::Rect;
|
use crate::math::rect::Rect;
|
||||||
|
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
|
@ -120,7 +120,11 @@ impl RgbaBitmap {
|
||||||
tint_color: u32,
|
tint_color: u32,
|
||||||
) {
|
) {
|
||||||
per_pixel_blit(
|
per_pixel_blit(
|
||||||
self, src, src_region, dest_x, dest_y,
|
self, //
|
||||||
|
src,
|
||||||
|
src_region,
|
||||||
|
dest_x,
|
||||||
|
dest_y,
|
||||||
|src_pixels, dest_pixels| {
|
|src_pixels, dest_pixels| {
|
||||||
*dest_pixels = tint_argb32(*src_pixels, tint_color);
|
*dest_pixels = tint_argb32(*src_pixels, tint_color);
|
||||||
},
|
},
|
||||||
|
@ -136,7 +140,11 @@ impl RgbaBitmap {
|
||||||
blend: BlendFunction,
|
blend: BlendFunction,
|
||||||
) {
|
) {
|
||||||
per_pixel_blit(
|
per_pixel_blit(
|
||||||
self, src, src_region, dest_x, dest_y,
|
self, //
|
||||||
|
src,
|
||||||
|
src_region,
|
||||||
|
dest_x,
|
||||||
|
dest_y,
|
||||||
|src_pixels, dest_pixels| {
|
|src_pixels, dest_pixels| {
|
||||||
*dest_pixels = blend.blend(*src_pixels, *dest_pixels);
|
*dest_pixels = blend.blend(*src_pixels, *dest_pixels);
|
||||||
},
|
},
|
||||||
|
@ -154,7 +162,13 @@ impl RgbaBitmap {
|
||||||
blend: BlendFunction,
|
blend: BlendFunction,
|
||||||
) {
|
) {
|
||||||
per_pixel_flipped_blit(
|
per_pixel_flipped_blit(
|
||||||
self, src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip,
|
self, //
|
||||||
|
src,
|
||||||
|
src_region,
|
||||||
|
dest_x,
|
||||||
|
dest_y,
|
||||||
|
horizontal_flip,
|
||||||
|
vertical_flip,
|
||||||
|src_pixels, dest_pixels| {
|
|src_pixels, dest_pixels| {
|
||||||
*dest_pixels = blend.blend(*src_pixels, *dest_pixels);
|
*dest_pixels = blend.blend(*src_pixels, *dest_pixels);
|
||||||
},
|
},
|
||||||
|
@ -172,7 +186,13 @@ impl RgbaBitmap {
|
||||||
tint_color: u32,
|
tint_color: u32,
|
||||||
) {
|
) {
|
||||||
per_pixel_flipped_blit(
|
per_pixel_flipped_blit(
|
||||||
self, src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip,
|
self, //
|
||||||
|
src,
|
||||||
|
src_region,
|
||||||
|
dest_x,
|
||||||
|
dest_y,
|
||||||
|
horizontal_flip,
|
||||||
|
vertical_flip,
|
||||||
|src_pixels, dest_pixels| {
|
|src_pixels, dest_pixels| {
|
||||||
*dest_pixels = tint_argb32(*src_pixels, tint_color);
|
*dest_pixels = tint_argb32(*src_pixels, tint_color);
|
||||||
},
|
},
|
||||||
|
@ -189,7 +209,11 @@ impl RgbaBitmap {
|
||||||
tint_color: u32,
|
tint_color: u32,
|
||||||
) {
|
) {
|
||||||
per_pixel_blit(
|
per_pixel_blit(
|
||||||
self, src, src_region, dest_x, dest_y,
|
self, //
|
||||||
|
src,
|
||||||
|
src_region,
|
||||||
|
dest_x,
|
||||||
|
dest_y,
|
||||||
|src_pixels, dest_pixels| {
|
|src_pixels, dest_pixels| {
|
||||||
if *src_pixels != transparent_color {
|
if *src_pixels != transparent_color {
|
||||||
*dest_pixels = tint_argb32(*src_pixels, tint_color);
|
*dest_pixels = tint_argb32(*src_pixels, tint_color);
|
||||||
|
@ -208,7 +232,11 @@ impl RgbaBitmap {
|
||||||
blend: BlendFunction,
|
blend: BlendFunction,
|
||||||
) {
|
) {
|
||||||
per_pixel_blit(
|
per_pixel_blit(
|
||||||
self, src, src_region, dest_x, dest_y,
|
self, //
|
||||||
|
src,
|
||||||
|
src_region,
|
||||||
|
dest_x,
|
||||||
|
dest_y,
|
||||||
|src_pixels, dest_pixels| {
|
|src_pixels, dest_pixels| {
|
||||||
if *src_pixels != transparent_color {
|
if *src_pixels != transparent_color {
|
||||||
*dest_pixels = blend.blend(*src_pixels, *dest_pixels);
|
*dest_pixels = blend.blend(*src_pixels, *dest_pixels);
|
||||||
|
@ -229,7 +257,13 @@ impl RgbaBitmap {
|
||||||
tint_color: u32,
|
tint_color: u32,
|
||||||
) {
|
) {
|
||||||
per_pixel_flipped_blit(
|
per_pixel_flipped_blit(
|
||||||
self, src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip,
|
self, //
|
||||||
|
src,
|
||||||
|
src_region,
|
||||||
|
dest_x,
|
||||||
|
dest_y,
|
||||||
|
horizontal_flip,
|
||||||
|
vertical_flip,
|
||||||
|src_pixels, dest_pixels| {
|
|src_pixels, dest_pixels| {
|
||||||
if *src_pixels != transparent_color {
|
if *src_pixels != transparent_color {
|
||||||
*dest_pixels = tint_argb32(*src_pixels, tint_color);
|
*dest_pixels = tint_argb32(*src_pixels, tint_color);
|
||||||
|
@ -250,7 +284,13 @@ impl RgbaBitmap {
|
||||||
blend: BlendFunction,
|
blend: BlendFunction,
|
||||||
) {
|
) {
|
||||||
per_pixel_flipped_blit(
|
per_pixel_flipped_blit(
|
||||||
self, src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip,
|
self, //
|
||||||
|
src,
|
||||||
|
src_region,
|
||||||
|
dest_x,
|
||||||
|
dest_y,
|
||||||
|
horizontal_flip,
|
||||||
|
vertical_flip,
|
||||||
|src_pixels, dest_pixels| {
|
|src_pixels, dest_pixels| {
|
||||||
if *src_pixels != transparent_color {
|
if *src_pixels != transparent_color {
|
||||||
*dest_pixels = blend.blend(*src_pixels, *dest_pixels);
|
*dest_pixels = blend.blend(*src_pixels, *dest_pixels);
|
||||||
|
@ -271,7 +311,14 @@ impl RgbaBitmap {
|
||||||
tint_color: u32,
|
tint_color: u32,
|
||||||
) {
|
) {
|
||||||
per_pixel_rotozoom_blit(
|
per_pixel_rotozoom_blit(
|
||||||
self, src, src_region, dest_x, dest_y, angle, scale_x, scale_y,
|
self, //
|
||||||
|
src,
|
||||||
|
src_region,
|
||||||
|
dest_x,
|
||||||
|
dest_y,
|
||||||
|
angle,
|
||||||
|
scale_x,
|
||||||
|
scale_y,
|
||||||
|src_pixel, dest_bitmap, draw_x, draw_y| {
|
|src_pixel, dest_bitmap, draw_x, draw_y| {
|
||||||
dest_bitmap.set_pixel(draw_x, draw_y, tint_argb32(src_pixel, tint_color));
|
dest_bitmap.set_pixel(draw_x, draw_y, tint_argb32(src_pixel, tint_color));
|
||||||
},
|
},
|
||||||
|
@ -290,7 +337,14 @@ impl RgbaBitmap {
|
||||||
blend: BlendFunction,
|
blend: BlendFunction,
|
||||||
) {
|
) {
|
||||||
per_pixel_rotozoom_blit(
|
per_pixel_rotozoom_blit(
|
||||||
self, src, src_region, dest_x, dest_y, angle, scale_x, scale_y,
|
self, //
|
||||||
|
src,
|
||||||
|
src_region,
|
||||||
|
dest_x,
|
||||||
|
dest_y,
|
||||||
|
angle,
|
||||||
|
scale_x,
|
||||||
|
scale_y,
|
||||||
|src_pixel, dest_bitmap, draw_x, draw_y| {
|
|src_pixel, dest_bitmap, draw_x, draw_y| {
|
||||||
if let Some(dest_pixel) = dest_bitmap.get_pixel(draw_x, draw_y) {
|
if let Some(dest_pixel) = dest_bitmap.get_pixel(draw_x, draw_y) {
|
||||||
dest_bitmap.set_pixel(draw_x, draw_y, blend.blend(src_pixel, dest_pixel))
|
dest_bitmap.set_pixel(draw_x, draw_y, blend.blend(src_pixel, dest_pixel))
|
||||||
|
@ -312,7 +366,14 @@ impl RgbaBitmap {
|
||||||
tint_color: u32,
|
tint_color: u32,
|
||||||
) {
|
) {
|
||||||
per_pixel_rotozoom_blit(
|
per_pixel_rotozoom_blit(
|
||||||
self, src, src_region, dest_x, dest_y, angle, scale_x, scale_y,
|
self, //
|
||||||
|
src,
|
||||||
|
src_region,
|
||||||
|
dest_x,
|
||||||
|
dest_y,
|
||||||
|
angle,
|
||||||
|
scale_x,
|
||||||
|
scale_y,
|
||||||
|src_pixel, dest_bitmap, draw_x, draw_y| {
|
|src_pixel, dest_bitmap, draw_x, draw_y| {
|
||||||
if transparent_color != src_pixel {
|
if transparent_color != src_pixel {
|
||||||
dest_bitmap.set_pixel(draw_x, draw_y, tint_argb32(src_pixel, tint_color));
|
dest_bitmap.set_pixel(draw_x, draw_y, tint_argb32(src_pixel, tint_color));
|
||||||
|
@ -334,7 +395,14 @@ impl RgbaBitmap {
|
||||||
blend: BlendFunction,
|
blend: BlendFunction,
|
||||||
) {
|
) {
|
||||||
per_pixel_rotozoom_blit(
|
per_pixel_rotozoom_blit(
|
||||||
self, src, src_region, dest_x, dest_y, angle, scale_x, scale_y,
|
self, //
|
||||||
|
src,
|
||||||
|
src_region,
|
||||||
|
dest_x,
|
||||||
|
dest_y,
|
||||||
|
angle,
|
||||||
|
scale_x,
|
||||||
|
scale_y,
|
||||||
|src_pixel, dest_bitmap, draw_x, draw_y| {
|
|src_pixel, dest_bitmap, draw_x, draw_y| {
|
||||||
if transparent_color != src_pixel {
|
if transparent_color != src_pixel {
|
||||||
if let Some(dest_pixel) = dest_bitmap.get_pixel(draw_x, draw_y) {
|
if let Some(dest_pixel) = dest_bitmap.get_pixel(draw_x, draw_y) {
|
||||||
|
@ -364,23 +432,23 @@ impl RgbaBitmap {
|
||||||
match method {
|
match method {
|
||||||
// rotozoom blits internally clip per-pixel right now ... and regardless, the normal
|
// rotozoom blits internally clip per-pixel right now ... and regardless, the normal
|
||||||
// clip_blit() function wouldn't handle a rotozoom blit destination region anyway ...
|
// clip_blit() function wouldn't handle a rotozoom blit destination region anyway ...
|
||||||
RotoZoom { .. } => {},
|
RotoZoom { .. } => {}
|
||||||
RotoZoomTinted { .. } => {},
|
RotoZoomTinted { .. } => {}
|
||||||
RotoZoomBlended { .. } => {},
|
RotoZoomBlended { .. } => {}
|
||||||
RotoZoomTransparent { .. } => {},
|
RotoZoomTransparent { .. } => {}
|
||||||
RotoZoomTransparentTinted { .. } => {},
|
RotoZoomTransparentTinted { .. } => {}
|
||||||
RotoZoomTransparentBlended { .. } => {},
|
RotoZoomTransparentBlended { .. } => {}
|
||||||
|
|
||||||
// set axis flip arguments
|
// set axis flip arguments
|
||||||
SolidFlipped { horizontal_flip, vertical_flip, .. } |
|
SolidFlipped { horizontal_flip, vertical_flip, .. }
|
||||||
SolidFlippedTinted { horizontal_flip, vertical_flip, .. } |
|
| SolidFlippedTinted { horizontal_flip, vertical_flip, .. }
|
||||||
SolidFlippedBlended { horizontal_flip, vertical_flip, .. } |
|
| SolidFlippedBlended { horizontal_flip, vertical_flip, .. }
|
||||||
TransparentFlipped { horizontal_flip, vertical_flip, .. } |
|
| TransparentFlipped { horizontal_flip, vertical_flip, .. }
|
||||||
TransparentFlippedTinted { horizontal_flip, vertical_flip, .. } |
|
| TransparentFlippedTinted { horizontal_flip, vertical_flip, .. }
|
||||||
TransparentFlippedBlended { horizontal_flip, vertical_flip, .. } |
|
| TransparentFlippedBlended { horizontal_flip, vertical_flip, .. }
|
||||||
TransparentFlippedSingle { horizontal_flip, vertical_flip, .. } => {
|
| TransparentFlippedSingle { horizontal_flip, vertical_flip, .. } => {
|
||||||
if !clip_blit(
|
if !clip_blit(
|
||||||
self.clip_region(),
|
self.clip_region(), //
|
||||||
&mut src_region,
|
&mut src_region,
|
||||||
&mut dest_x,
|
&mut dest_x,
|
||||||
&mut dest_y,
|
&mut dest_y,
|
||||||
|
@ -394,7 +462,7 @@ impl RgbaBitmap {
|
||||||
// otherwise clip like normal!
|
// otherwise clip like normal!
|
||||||
_ => {
|
_ => {
|
||||||
if !clip_blit(
|
if !clip_blit(
|
||||||
self.clip_region(),
|
self.clip_region(), //
|
||||||
&mut src_region,
|
&mut src_region,
|
||||||
&mut dest_x,
|
&mut dest_x,
|
||||||
&mut dest_y,
|
&mut dest_y,
|
||||||
|
@ -500,7 +568,14 @@ impl RgbaBitmap {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn blit_atlas_unchecked(&mut self, method: RgbaBlitMethod, src: &BitmapAtlas<Self>, index: usize, x: i32, y: i32) {
|
pub unsafe fn blit_atlas_unchecked(
|
||||||
|
&mut self,
|
||||||
|
method: RgbaBlitMethod,
|
||||||
|
src: &BitmapAtlas<Self>,
|
||||||
|
index: usize,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
) {
|
||||||
if let Some(src_region) = src.get(index) {
|
if let Some(src_region) = src.get(index) {
|
||||||
self.blit_region_unchecked(method, src.bitmap(), &src_region, x, y);
|
self.blit_region_unchecked(method, src.bitmap(), &src_region, x, y);
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,23 +30,19 @@ impl RgbaBitmap {
|
||||||
Some("pcx") => {
|
Some("pcx") => {
|
||||||
let (bmp, palette) = Self::load_pcx_file(path)?;
|
let (bmp, palette) = Self::load_pcx_file(path)?;
|
||||||
Ok((bmp, Some(palette)))
|
Ok((bmp, Some(palette)))
|
||||||
},
|
}
|
||||||
Some("gif") => {
|
Some("gif") => {
|
||||||
let (bmp, palette) = Self::load_gif_file(path)?;
|
let (bmp, palette) = Self::load_gif_file(path)?;
|
||||||
Ok((bmp, Some(palette)))
|
Ok((bmp, Some(palette)))
|
||||||
},
|
}
|
||||||
Some("iff") | Some("lbm") | Some("pbm") | Some("bbm") => {
|
Some("iff") | Some("lbm") | Some("pbm") | Some("bbm") => {
|
||||||
let (bmp, palette) = Self::load_iff_file(path)?;
|
let (bmp, palette) = Self::load_iff_file(path)?;
|
||||||
Ok((bmp, Some(palette)))
|
Ok((bmp, Some(palette)))
|
||||||
}
|
}
|
||||||
_ => Err(BitmapError::UnknownFileType(String::from(
|
_ => Err(BitmapError::UnknownFileType(String::from("Unrecognized file extension"))),
|
||||||
"Unrecognized file extension",
|
|
||||||
))),
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(BitmapError::UnknownFileType(String::from(
|
Err(BitmapError::UnknownFileType(String::from("No file extension")))
|
||||||
"No file extension",
|
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,10 +7,9 @@ impl RgbaBitmap {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_blended_pixel(&mut self, x: i32, y: i32, color: u32, blend: BlendFunction) {
|
pub fn set_blended_pixel(&mut self, x: i32, y: i32, color: u32, blend: BlendFunction) {
|
||||||
self.set_custom_pixel(
|
self.set_custom_pixel(
|
||||||
x, y,
|
x, //
|
||||||
|dest_color| {
|
y,
|
||||||
blend.blend(color, dest_color)
|
|dest_color| blend.blend(color, dest_color),
|
||||||
}
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,20 +19,20 @@ impl RgbaBitmap {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn set_blended_pixel_unchecked(&mut self, x: i32, y: i32, color: u32, blend: BlendFunction) {
|
pub unsafe fn set_blended_pixel_unchecked(&mut self, x: i32, y: i32, color: u32, blend: BlendFunction) {
|
||||||
self.set_custom_pixel_unchecked(
|
self.set_custom_pixel_unchecked(
|
||||||
x, y,
|
x, //
|
||||||
|dest_color| {
|
y,
|
||||||
blend.blend(color, dest_color)
|
|dest_color| blend.blend(color, dest_color),
|
||||||
}
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draws a line from x1,y1 to x2,y2 by blending the drawn pixels using the given blend function.
|
/// Draws a line from x1,y1 to x2,y2 by blending the drawn pixels using the given blend function.
|
||||||
pub fn blended_line(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: u32, blend: BlendFunction) {
|
pub fn blended_line(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: u32, blend: BlendFunction) {
|
||||||
self.line_custom(
|
self.line_custom(
|
||||||
x1, y1, x2, y2,
|
x1, //
|
||||||
|dest_color| {
|
y1,
|
||||||
blend.blend(color, dest_color)
|
x2,
|
||||||
}
|
y2,
|
||||||
|
|dest_color| blend.blend(color, dest_color),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,10 +40,10 @@ impl RgbaBitmap {
|
||||||
/// blend function.
|
/// blend function.
|
||||||
pub fn blended_horiz_line(&mut self, x1: i32, x2: i32, y: i32, color: u32, blend: BlendFunction) {
|
pub fn blended_horiz_line(&mut self, x1: i32, x2: i32, y: i32, color: u32, blend: BlendFunction) {
|
||||||
self.horiz_line_custom(
|
self.horiz_line_custom(
|
||||||
x1, x2, y,
|
x1, //
|
||||||
|dest_color| {
|
x2,
|
||||||
blend.blend(color, dest_color)
|
y,
|
||||||
}
|
|dest_color| blend.blend(color, dest_color),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,10 +51,10 @@ impl RgbaBitmap {
|
||||||
/// function.
|
/// function.
|
||||||
pub fn blended_vert_line(&mut self, x: i32, y1: i32, y2: i32, color: u32, blend: BlendFunction) {
|
pub fn blended_vert_line(&mut self, x: i32, y1: i32, y2: i32, color: u32, blend: BlendFunction) {
|
||||||
self.vert_line_custom(
|
self.vert_line_custom(
|
||||||
x, y1, y2,
|
x, //
|
||||||
|dest_color| {
|
y1,
|
||||||
blend.blend(color, dest_color)
|
y2,
|
||||||
}
|
|dest_color| blend.blend(color, dest_color),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,10 +63,11 @@ impl RgbaBitmap {
|
||||||
/// The box is drawn by blending the drawn pixels using the given blend function.
|
/// The box is drawn by blending the drawn pixels using the given blend function.
|
||||||
pub fn blended_rect(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: u32, blend: BlendFunction) {
|
pub fn blended_rect(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: u32, blend: BlendFunction) {
|
||||||
self.rect_custom(
|
self.rect_custom(
|
||||||
x1, y1, x2, y2,
|
x1, //
|
||||||
|dest_color| {
|
y1,
|
||||||
blend.blend(color, dest_color)
|
x2,
|
||||||
}
|
y2,
|
||||||
|
|dest_color| blend.blend(color, dest_color),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,10 +76,11 @@ impl RgbaBitmap {
|
||||||
/// filled box is draw by blending the drawn pixels using the given blend function.
|
/// filled box is draw by blending the drawn pixels using the given blend function.
|
||||||
pub fn blended_filled_rect(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: u32, blend: BlendFunction) {
|
pub fn blended_filled_rect(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: u32, blend: BlendFunction) {
|
||||||
self.filled_rect_custom(
|
self.filled_rect_custom(
|
||||||
x1, y1, x2, y2,
|
x1, //
|
||||||
|dest_color| {
|
y1,
|
||||||
blend.blend(color, dest_color)
|
x2,
|
||||||
}
|
y2,
|
||||||
|
|dest_color| blend.blend(color, dest_color),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ pub enum BitmapAtlasError {
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub struct BitmapAtlas<BitmapType>
|
pub struct BitmapAtlas<BitmapType>
|
||||||
where
|
where
|
||||||
BitmapType: GeneralBitmap
|
BitmapType: GeneralBitmap,
|
||||||
{
|
{
|
||||||
bitmap: BitmapType,
|
bitmap: BitmapType,
|
||||||
bounds: Rect,
|
bounds: Rect,
|
||||||
|
@ -23,12 +23,12 @@ where
|
||||||
|
|
||||||
impl<BitmapType> BitmapAtlas<BitmapType>
|
impl<BitmapType> BitmapAtlas<BitmapType>
|
||||||
where
|
where
|
||||||
BitmapType: GeneralBitmap
|
BitmapType: GeneralBitmap,
|
||||||
{
|
{
|
||||||
pub fn new(bitmap: BitmapType) -> Self {
|
pub fn new(bitmap: BitmapType) -> Self {
|
||||||
let bounds = bitmap.full_bounds();
|
let bounds = bitmap.full_bounds();
|
||||||
BitmapAtlas {
|
BitmapAtlas {
|
||||||
bitmap,
|
bitmap, //
|
||||||
bounds,
|
bounds,
|
||||||
tiles: Vec::new(),
|
tiles: Vec::new(),
|
||||||
}
|
}
|
||||||
|
@ -43,11 +43,7 @@ where
|
||||||
Ok(self.tiles.len() - 1)
|
Ok(self.tiles.len() - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_grid(
|
pub fn add_grid(&mut self, tile_width: u32, tile_height: u32) -> Result<usize, BitmapAtlasError> {
|
||||||
&mut self,
|
|
||||||
tile_width: u32,
|
|
||||||
tile_height: u32,
|
|
||||||
) -> Result<usize, BitmapAtlasError> {
|
|
||||||
if self.bounds.width < tile_width || self.bounds.height < tile_height {
|
if self.bounds.width < tile_width || self.bounds.height < tile_height {
|
||||||
return Err(BitmapAtlasError::OutOfBounds);
|
return Err(BitmapAtlasError::OutOfBounds);
|
||||||
}
|
}
|
||||||
|
@ -121,7 +117,8 @@ where
|
||||||
|
|
||||||
impl<BitmapType> Index<usize> for BitmapAtlas<BitmapType>
|
impl<BitmapType> Index<usize> for BitmapAtlas<BitmapType>
|
||||||
where
|
where
|
||||||
BitmapType: GeneralBitmap {
|
BitmapType: GeneralBitmap,
|
||||||
|
{
|
||||||
type Output = Rect;
|
type Output = Rect;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -153,22 +150,13 @@ pub mod tests {
|
||||||
assert_eq!(rect, atlas[1]);
|
assert_eq!(rect, atlas[1]);
|
||||||
assert_eq!(2, atlas.len());
|
assert_eq!(2, atlas.len());
|
||||||
|
|
||||||
assert_matches!(
|
assert_matches!(atlas.add(Rect::new(56, 0, 16, 16)), Err(BitmapAtlasError::OutOfBounds));
|
||||||
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));
|
||||||
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));
|
||||||
atlas.add(Rect::new(0, 0, 128, 128)),
|
|
||||||
Err(BitmapAtlasError::OutOfBounds)
|
|
||||||
);
|
|
||||||
assert_eq!(2, atlas.len());
|
assert_eq!(2, atlas.len());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@ pub struct BlendMap {
|
||||||
|
|
||||||
impl std::fmt::Debug for BlendMap {
|
impl std::fmt::Debug for BlendMap {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
f.debug_struct("BlendMap")
|
f.debug_struct("BlendMap") //
|
||||||
.field("start_color", &self.start_color)
|
.field("start_color", &self.start_color)
|
||||||
.field("end_color", &self.end_color)
|
.field("end_color", &self.end_color)
|
||||||
.finish_non_exhaustive()
|
.finish_non_exhaustive()
|
||||||
|
@ -58,17 +58,10 @@ impl BlendMap {
|
||||||
/// range only. The `start_color` and `end_color` may also be equal to create a blend map with
|
/// range only. The `start_color` and `end_color` may also be equal to create a blend map with
|
||||||
/// only a single source color mapping.
|
/// only a single source color mapping.
|
||||||
pub fn new(start_color: u8, end_color: u8) -> Self {
|
pub fn new(start_color: u8, end_color: u8) -> Self {
|
||||||
let (start_color, end_color) = if start_color > end_color {
|
let (start_color, end_color) =
|
||||||
(end_color, start_color)
|
if start_color > end_color { (end_color, start_color) } else { (start_color, end_color) };
|
||||||
} else {
|
|
||||||
(start_color, end_color)
|
|
||||||
};
|
|
||||||
let num_colors = (end_color - start_color) as usize + 1;
|
let num_colors = (end_color - start_color) as usize + 1;
|
||||||
BlendMap {
|
BlendMap { start_color, end_color, mapping: vec![[0u8; 256]; num_colors].into_boxed_slice() }
|
||||||
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
|
/// Creates and returns a new [`BlendMap`] with a single source color mapping which maps to
|
||||||
|
@ -77,11 +70,8 @@ impl BlendMap {
|
||||||
/// like a simple translucency effect. The starting color in the gradient is used as the source
|
/// like a simple translucency effect. The starting color in the gradient is used as the source
|
||||||
/// color mapping in the returned blend map.
|
/// color mapping in the returned blend map.
|
||||||
pub fn new_colorized_map(gradient_start: u8, gradient_end: u8, palette: &Palette) -> Self {
|
pub fn new_colorized_map(gradient_start: u8, gradient_end: u8, palette: &Palette) -> Self {
|
||||||
let (gradient_start, gradient_end) = if gradient_start > gradient_end {
|
let (gradient_start, gradient_end) =
|
||||||
(gradient_end, gradient_start)
|
if gradient_start > gradient_end { (gradient_end, gradient_start) } else { (gradient_start, gradient_end) };
|
||||||
} else {
|
|
||||||
(gradient_start, gradient_end)
|
|
||||||
};
|
|
||||||
let gradient_size = gradient_end - gradient_start + 1;
|
let gradient_size = gradient_end - gradient_start + 1;
|
||||||
let source_color = gradient_start;
|
let source_color = gradient_start;
|
||||||
|
|
||||||
|
@ -89,11 +79,13 @@ impl BlendMap {
|
||||||
for idx in 0..=255 {
|
for idx in 0..=255 {
|
||||||
let (r, g, b) = from_rgb32(palette[idx]);
|
let (r, g, b) = from_rgb32(palette[idx]);
|
||||||
let lit = (luminance(r, g, b) * 255.0) as u8;
|
let lit = (luminance(r, g, b) * 255.0) as u8;
|
||||||
blend_map.set_mapping(
|
blend_map
|
||||||
source_color,
|
.set_mapping(
|
||||||
idx as u8,
|
source_color,
|
||||||
(gradient_size - 1) - (lit / (256 / gradient_size as u32) as u8) + source_color,
|
idx as u8,
|
||||||
).unwrap();
|
(gradient_size - 1) - (lit / (256 / gradient_size as u32) as u8) + source_color,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
blend_map
|
blend_map
|
||||||
}
|
}
|
||||||
|
@ -108,11 +100,8 @@ impl BlendMap {
|
||||||
palette: &Palette,
|
palette: &Palette,
|
||||||
f: impl Fn(f32, f32) -> f32,
|
f: impl Fn(f32, f32) -> f32,
|
||||||
) -> BlendMap {
|
) -> BlendMap {
|
||||||
let (gradient_start, gradient_end) = if gradient_start > gradient_end {
|
let (gradient_start, gradient_end) =
|
||||||
(gradient_end, gradient_start)
|
if gradient_start > gradient_end { (gradient_end, gradient_start) } else { (gradient_start, gradient_end) };
|
||||||
} else {
|
|
||||||
(gradient_start, gradient_end)
|
|
||||||
};
|
|
||||||
let gradient_size = gradient_end - gradient_start + 1;
|
let gradient_size = gradient_end - gradient_start + 1;
|
||||||
|
|
||||||
let mut blend_map = BlendMap::new(0, 255);
|
let mut blend_map = BlendMap::new(0, 255);
|
||||||
|
@ -123,11 +112,13 @@ impl BlendMap {
|
||||||
let (r, g, b) = from_rgb32(palette[dest_color]);
|
let (r, g, b) = from_rgb32(palette[dest_color]);
|
||||||
let destination_luminance = luminance(r, g, b);
|
let destination_luminance = luminance(r, g, b);
|
||||||
let weight = (f(source_luminance, destination_luminance) * 255.0) as u8;
|
let weight = (f(source_luminance, destination_luminance) * 255.0) as u8;
|
||||||
blend_map.set_mapping(
|
blend_map
|
||||||
source_color,
|
.set_mapping(
|
||||||
dest_color,
|
source_color,
|
||||||
(gradient_size - 1).wrapping_sub(weight / (256 / gradient_size as u32) as u8) + gradient_start,
|
dest_color,
|
||||||
).unwrap();
|
(gradient_size - 1).wrapping_sub(weight / (256 / gradient_size as u32) as u8) + gradient_start,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
blend_map
|
blend_map
|
||||||
|
@ -223,12 +214,19 @@ impl BlendMap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Sets a series of blend color mappings for the given source color and starting from a base
|
/// Sets a series of blend color mappings for the given source color and starting from a base
|
||||||
/// destination color.
|
/// destination color.
|
||||||
pub fn set_mappings<const N: usize>(&mut self, source_color: u8, base_dest_color: u8, mappings: [u8; N]) -> Result<(), BlendMapError> {
|
pub fn set_mappings<const N: usize>(
|
||||||
|
&mut self,
|
||||||
|
source_color: u8,
|
||||||
|
base_dest_color: u8,
|
||||||
|
mappings: [u8; N],
|
||||||
|
) -> Result<(), BlendMapError> {
|
||||||
if let Some(mapping) = self.get_mapping_mut(source_color) {
|
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");
|
assert!(
|
||||||
|
(base_dest_color as usize + N - 1) <= 255,
|
||||||
|
"mappings array is too big for the remaining colors available"
|
||||||
|
);
|
||||||
for index in 0..N {
|
for index in 0..N {
|
||||||
mapping[index + base_dest_color as usize] = mappings[index];
|
mapping[index + base_dest_color as usize] = mappings[index];
|
||||||
}
|
}
|
||||||
|
@ -272,7 +270,7 @@ impl BlendMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(BlendMap {
|
Ok(BlendMap {
|
||||||
start_color,
|
start_color, //
|
||||||
end_color,
|
end_color,
|
||||||
mapping: maps.into_boxed_slice(),
|
mapping: maps.into_boxed_slice(),
|
||||||
})
|
})
|
||||||
|
@ -368,10 +366,7 @@ mod tests {
|
||||||
mapping[0] = 217;
|
mapping[0] = 217;
|
||||||
assert_eq!(Some(217), blend_map.blend(17, 0));
|
assert_eq!(Some(217), blend_map.blend(17, 0));
|
||||||
|
|
||||||
assert_matches!(
|
assert_matches!(blend_map.set_mapping(64, 1, 2), Err(BlendMapError::InvalidSourceColor(64)));
|
||||||
blend_map.set_mapping(64, 1, 2),
|
|
||||||
Err(BlendMapError::InvalidSourceColor(64))
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -386,11 +381,7 @@ mod tests {
|
||||||
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();
|
let mapping = blend_map.get_mapping(2).unwrap();
|
||||||
assert_eq!(
|
assert_eq!([0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0], mapping[0..16]);
|
||||||
[0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0],
|
|
||||||
mapping[0..16]
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -413,4 +404,4 @@ mod tests {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ impl BlendFunction {
|
||||||
TintedBlend(tint) => {
|
TintedBlend(tint) => {
|
||||||
let tinted = tint_argb32(src, *tint);
|
let tinted = tint_argb32(src, *tint);
|
||||||
blend_argb32(tinted, dest)
|
blend_argb32(tinted, dest)
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -257,9 +257,9 @@ fn srgb_to_linearized(color_channel: u8) -> f32 {
|
||||||
|
|
||||||
/// Calculates the given sRGB color's luminance, returned as a value between 0.0 and 1.0.
|
/// 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 {
|
pub fn luminance(r: u8, g: u8, b: u8) -> f32 {
|
||||||
(LUMINANCE_RED * srgb_to_linearized(r)) +
|
(LUMINANCE_RED * srgb_to_linearized(r))
|
||||||
(LUMINANCE_GREEN * srgb_to_linearized(g)) +
|
+ (LUMINANCE_GREEN * srgb_to_linearized(g))
|
||||||
(LUMINANCE_BLUE * srgb_to_linearized(b))
|
+ (LUMINANCE_BLUE * srgb_to_linearized(b))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn brightness(mut luminance: f32) -> f32 {
|
fn brightness(mut luminance: f32) -> f32 {
|
||||||
|
|
|
@ -62,7 +62,7 @@ impl Character for BitmaskCharacter {
|
||||||
|
|
||||||
fn draw<PixelType>(&self, dest: &mut Bitmap<PixelType>, x: i32, y: i32, opts: FontRenderOpts<PixelType>)
|
fn draw<PixelType>(&self, dest: &mut Bitmap<PixelType>, x: i32, y: i32, opts: FontRenderOpts<PixelType>)
|
||||||
where
|
where
|
||||||
PixelType: Pixel
|
PixelType: Pixel,
|
||||||
{
|
{
|
||||||
// out of bounds check
|
// out of bounds check
|
||||||
if ((x + self.bounds.width as i32) < dest.clip_region().x)
|
if ((x + self.bounds.width as i32) < dest.clip_region().x)
|
||||||
|
@ -104,7 +104,7 @@ pub struct BitmaskFont {
|
||||||
|
|
||||||
impl std::fmt::Debug for BitmaskFont {
|
impl std::fmt::Debug for BitmaskFont {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
f.debug_struct("BitmaskFont")
|
f.debug_struct("BitmaskFont") //
|
||||||
.field("line_height", &self.line_height)
|
.field("line_height", &self.line_height)
|
||||||
.field("space_width", &self.space_width)
|
.field("space_width", &self.space_width)
|
||||||
.field("characters.len()", &self.characters.len())
|
.field("characters.len()", &self.characters.len())
|
||||||
|
@ -134,12 +134,7 @@ impl BitmaskFont {
|
||||||
let character = BitmaskCharacter {
|
let character = BitmaskCharacter {
|
||||||
bytes: buffer,
|
bytes: buffer,
|
||||||
// bounds are filled in below. ugh.
|
// bounds are filled in below. ugh.
|
||||||
bounds: Rect {
|
bounds: Rect { x: 0, y: 0, width: 0, height: 0 },
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
width: 0,
|
|
||||||
height: 0,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
characters.push(character);
|
characters.push(character);
|
||||||
}
|
}
|
||||||
|
@ -158,7 +153,7 @@ impl BitmaskFont {
|
||||||
let space_width = characters[' ' as usize].bounds.width as u8;
|
let space_width = characters[' ' as usize].bounds.width as u8;
|
||||||
|
|
||||||
Ok(BitmaskFont {
|
Ok(BitmaskFont {
|
||||||
characters: characters.into_boxed_slice(),
|
characters: characters.into_boxed_slice(), //
|
||||||
line_height,
|
line_height,
|
||||||
space_width,
|
space_width,
|
||||||
})
|
})
|
||||||
|
@ -208,7 +203,7 @@ impl Font for BitmaskFont {
|
||||||
|
|
||||||
fn measure<PixelType>(&self, text: &str, _opts: FontRenderOpts<PixelType>) -> (u32, u32)
|
fn measure<PixelType>(&self, text: &str, _opts: FontRenderOpts<PixelType>) -> (u32, u32)
|
||||||
where
|
where
|
||||||
PixelType: Pixel
|
PixelType: Pixel,
|
||||||
{
|
{
|
||||||
if text.is_empty() {
|
if text.is_empty() {
|
||||||
return (0, 0);
|
return (0, 0);
|
||||||
|
|
|
@ -2,9 +2,9 @@ use num_traits::{PrimInt, Unsigned};
|
||||||
|
|
||||||
pub mod bitmap;
|
pub mod bitmap;
|
||||||
pub mod bitmapatlas;
|
pub mod bitmapatlas;
|
||||||
|
pub mod blendmap;
|
||||||
pub mod color;
|
pub mod color;
|
||||||
pub mod font;
|
pub mod font;
|
||||||
pub mod blendmap;
|
|
||||||
pub mod palette;
|
pub mod palette;
|
||||||
|
|
||||||
pub mod prelude;
|
pub mod prelude;
|
||||||
|
|
|
@ -14,8 +14,8 @@ use crate::utils::abs_diff;
|
||||||
const NUM_COLORS: usize = 256;
|
const NUM_COLORS: usize = 256;
|
||||||
|
|
||||||
/// Common trait to represent a range of indexed colour values.
|
/// Common trait to represent a range of indexed colour values.
|
||||||
pub trait ColorRange: RangeBounds<u8> + Iterator<Item=u8> {}
|
pub trait ColorRange: RangeBounds<u8> + Iterator<Item = u8> {}
|
||||||
impl<T> ColorRange for T where T: RangeBounds<u8> + Iterator<Item=u8> {}
|
impl<T> ColorRange for T where T: RangeBounds<u8> + Iterator<Item = u8> {}
|
||||||
|
|
||||||
pub static VGA_PALETTE_BYTES: &[u8] = include_bytes!("../../assets/vga.pal");
|
pub static VGA_PALETTE_BYTES: &[u8] = include_bytes!("../../assets/vga.pal");
|
||||||
|
|
||||||
|
@ -30,10 +30,7 @@ fn to_6bit(value: u8) -> u8 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// vga bios (0-63) format
|
// vga bios (0-63) format
|
||||||
fn read_palette_6bit<T: ReadBytesExt>(
|
fn read_palette_6bit<T: ReadBytesExt>(reader: &mut T, num_colors: usize) -> Result<[u32; NUM_COLORS], PaletteError> {
|
||||||
reader: &mut T,
|
|
||||||
num_colors: usize,
|
|
||||||
) -> Result<[u32; NUM_COLORS], PaletteError> {
|
|
||||||
if num_colors > NUM_COLORS {
|
if num_colors > NUM_COLORS {
|
||||||
return Err(PaletteError::OutOfRange(num_colors));
|
return Err(PaletteError::OutOfRange(num_colors));
|
||||||
}
|
}
|
||||||
|
@ -42,11 +39,7 @@ fn read_palette_6bit<T: ReadBytesExt>(
|
||||||
let r = reader.read_u8()?;
|
let r = reader.read_u8()?;
|
||||||
let g = reader.read_u8()?;
|
let g = reader.read_u8()?;
|
||||||
let b = reader.read_u8()?;
|
let b = reader.read_u8()?;
|
||||||
let color = to_rgb32(
|
let color = to_rgb32(from_6bit(r), from_6bit(g), from_6bit(b));
|
||||||
from_6bit(r),
|
|
||||||
from_6bit(g),
|
|
||||||
from_6bit(b)
|
|
||||||
);
|
|
||||||
colors[i as usize] = color;
|
colors[i as usize] = color;
|
||||||
}
|
}
|
||||||
Ok(colors)
|
Ok(colors)
|
||||||
|
@ -70,10 +63,7 @@ fn write_palette_6bit<T: WriteBytesExt>(
|
||||||
}
|
}
|
||||||
|
|
||||||
// normal (0-255) format
|
// normal (0-255) format
|
||||||
fn read_palette_8bit<T: ReadBytesExt>(
|
fn read_palette_8bit<T: ReadBytesExt>(reader: &mut T, num_colors: usize) -> Result<[u32; NUM_COLORS], PaletteError> {
|
||||||
reader: &mut T,
|
|
||||||
num_colors: usize,
|
|
||||||
) -> Result<[u32; NUM_COLORS], PaletteError> {
|
|
||||||
if num_colors > NUM_COLORS {
|
if num_colors > NUM_COLORS {
|
||||||
return Err(PaletteError::OutOfRange(num_colors));
|
return Err(PaletteError::OutOfRange(num_colors));
|
||||||
}
|
}
|
||||||
|
@ -131,17 +121,13 @@ pub struct Palette {
|
||||||
impl Palette {
|
impl Palette {
|
||||||
/// Creates a new Palette with all black colors.
|
/// Creates a new Palette with all black colors.
|
||||||
pub fn new() -> Palette {
|
pub fn new() -> Palette {
|
||||||
Palette {
|
Palette { colors: [0; NUM_COLORS] }
|
||||||
colors: [0; NUM_COLORS],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new Palette with all initial colors having the RGB values specified.
|
/// 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 {
|
pub fn new_with_default(r: u8, g: u8, b: u8) -> Palette {
|
||||||
let rgb = to_rgb32(r, g, b);
|
let rgb = to_rgb32(r, g, b);
|
||||||
Palette {
|
Palette { colors: [rgb; NUM_COLORS] }
|
||||||
colors: [rgb; NUM_COLORS],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new Palette, pre-loaded with the default VGA BIOS colors.
|
/// Creates a new Palette, pre-loaded with the default VGA BIOS colors.
|
||||||
|
@ -168,10 +154,7 @@ impl Palette {
|
||||||
///
|
///
|
||||||
/// * `reader`: the reader to load the palette from
|
/// * `reader`: the reader to load the palette from
|
||||||
/// * `format`: the format that the palette data is expected to be in
|
/// * `format`: the format that the palette data is expected to be in
|
||||||
pub fn load_from_bytes<T: ReadBytesExt>(
|
pub fn load_from_bytes<T: ReadBytesExt>(reader: &mut T, format: PaletteFormat) -> Result<Palette, PaletteError> {
|
||||||
reader: &mut T,
|
|
||||||
format: PaletteFormat,
|
|
||||||
) -> Result<Palette, PaletteError> {
|
|
||||||
let colors = match format {
|
let colors = match format {
|
||||||
PaletteFormat::Vga => read_palette_6bit(reader, NUM_COLORS)?,
|
PaletteFormat::Vga => read_palette_6bit(reader, NUM_COLORS)?,
|
||||||
PaletteFormat::Normal => read_palette_8bit(reader, NUM_COLORS)?,
|
PaletteFormat::Normal => read_palette_8bit(reader, NUM_COLORS)?,
|
||||||
|
@ -241,11 +224,7 @@ impl Palette {
|
||||||
///
|
///
|
||||||
/// * `writer`: the writer to write palette data to
|
/// * `writer`: the writer to write palette data to
|
||||||
/// * `format`: the format to write the palette data in
|
/// * `format`: the format to write the palette data in
|
||||||
pub fn to_bytes<T: WriteBytesExt>(
|
pub fn to_bytes<T: WriteBytesExt>(&self, writer: &mut T, format: PaletteFormat) -> Result<(), PaletteError> {
|
||||||
&self,
|
|
||||||
writer: &mut T,
|
|
||||||
format: PaletteFormat,
|
|
||||||
) -> Result<(), PaletteError> {
|
|
||||||
match format {
|
match format {
|
||||||
PaletteFormat::Vga => write_palette_6bit(writer, &self.colors, NUM_COLORS),
|
PaletteFormat::Vga => write_palette_6bit(writer, &self.colors, NUM_COLORS),
|
||||||
PaletteFormat::Normal => write_palette_8bit(writer, &self.colors, NUM_COLORS),
|
PaletteFormat::Normal => write_palette_8bit(writer, &self.colors, NUM_COLORS),
|
||||||
|
@ -314,14 +293,7 @@ impl Palette {
|
||||||
/// * `step`: the amount to "step" by towards the target RGB values
|
/// * `step`: the amount to "step" by towards the target RGB values
|
||||||
///
|
///
|
||||||
/// returns: true if the color has reached the target RGB values, false otherwise
|
/// returns: true if the color has reached the target RGB values, false otherwise
|
||||||
pub fn fade_color_toward_rgb(
|
pub fn fade_color_toward_rgb(&mut self, color: u8, target_r: u8, target_g: u8, target_b: u8, step: u8) -> bool {
|
||||||
&mut self,
|
|
||||||
color: u8,
|
|
||||||
target_r: u8,
|
|
||||||
target_g: u8,
|
|
||||||
target_b: u8,
|
|
||||||
step: u8,
|
|
||||||
) -> bool {
|
|
||||||
let mut modified = false;
|
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]);
|
||||||
|
@ -407,12 +379,7 @@ impl Palette {
|
||||||
///
|
///
|
||||||
/// returns: true if all of the colors in the range have reached the RGB values from the other
|
/// returns: true if all of the colors in the range have reached the RGB values from the other
|
||||||
/// target palette, false otherwise
|
/// target palette, false otherwise
|
||||||
pub fn fade_colors_toward_palette<T: ColorRange>(
|
pub fn fade_colors_toward_palette<T: ColorRange>(&mut self, colors: T, palette: &Palette, step: u8) -> bool {
|
||||||
&mut self,
|
|
||||||
colors: T,
|
|
||||||
palette: &Palette,
|
|
||||||
step: u8,
|
|
||||||
) -> bool {
|
|
||||||
let mut all_faded = true;
|
let mut all_faded = true;
|
||||||
for color in colors {
|
for color in colors {
|
||||||
let (r, g, b) = from_rgb32(palette[color]);
|
let (r, g, b) = from_rgb32(palette[color]);
|
||||||
|
@ -481,9 +448,7 @@ impl Palette {
|
||||||
// this comparison method is using the sRGB Euclidean formula described here:
|
// this comparison method is using the sRGB Euclidean formula described here:
|
||||||
// https://en.wikipedia.org/wiki/Color_difference
|
// https://en.wikipedia.org/wiki/Color_difference
|
||||||
|
|
||||||
let distance = abs_diff(this_r, r) 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;
|
||||||
+ abs_diff(this_g, g) as u32
|
|
||||||
+ abs_diff(this_b, b) as u32;
|
|
||||||
|
|
||||||
if distance < closest_distance {
|
if distance < closest_distance {
|
||||||
closest = index as u8;
|
closest = index as u8;
|
||||||
|
@ -597,10 +562,7 @@ mod tests {
|
||||||
|
|
||||||
// vga rgb format (6-bit)
|
// vga rgb format (6-bit)
|
||||||
|
|
||||||
let palette = Palette::load_from_file(
|
let palette = Palette::load_from_file(test_file(Path::new("vga.pal")).as_path(), PaletteFormat::Vga)?;
|
||||||
test_file(Path::new("vga.pal")).as_path(),
|
|
||||||
PaletteFormat::Vga
|
|
||||||
)?;
|
|
||||||
assert_ega_colors(&palette);
|
assert_ega_colors(&palette);
|
||||||
|
|
||||||
let save_path = tmp_dir.path().join("test_save_vga_format.pal");
|
let save_path = tmp_dir.path().join("test_save_vga_format.pal");
|
||||||
|
@ -610,10 +572,7 @@ mod tests {
|
||||||
|
|
||||||
// normal rgb format (8-bit)
|
// normal rgb format (8-bit)
|
||||||
|
|
||||||
let palette = Palette::load_from_file(
|
let palette = Palette::load_from_file(test_file(Path::new("dp2.pal")).as_path(), PaletteFormat::Normal)?;
|
||||||
test_file(Path::new("dp2.pal")).as_path(),
|
|
||||||
PaletteFormat::Normal
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let save_path = tmp_dir.path().join("test_save_normal_format.pal");
|
let save_path = tmp_dir.path().join("test_save_normal_format.pal");
|
||||||
palette.to_file(&save_path, PaletteFormat::Normal)?;
|
palette.to_file(&save_path, PaletteFormat::Normal)?;
|
||||||
|
@ -629,11 +588,8 @@ mod tests {
|
||||||
|
|
||||||
// vga rgb format (6-bit)
|
// vga rgb format (6-bit)
|
||||||
|
|
||||||
let palette = Palette::load_num_colors_from_file(
|
let palette =
|
||||||
test_file(Path::new("ega_6bit.pal")).as_path(),
|
Palette::load_num_colors_from_file(test_file(Path::new("ega_6bit.pal")).as_path(), PaletteFormat::Vga, 16)?;
|
||||||
PaletteFormat::Vga,
|
|
||||||
16
|
|
||||||
)?;
|
|
||||||
assert_ega_colors(&palette);
|
assert_ega_colors(&palette);
|
||||||
|
|
||||||
let save_path = tmp_dir.path().join("test_save_vga_format_16_colors.pal");
|
let save_path = tmp_dir.path().join("test_save_vga_format_16_colors.pal");
|
||||||
|
@ -646,7 +602,7 @@ mod tests {
|
||||||
let palette = Palette::load_num_colors_from_file(
|
let palette = Palette::load_num_colors_from_file(
|
||||||
test_file(Path::new("ega_8bit.pal")).as_path(),
|
test_file(Path::new("ega_8bit.pal")).as_path(),
|
||||||
PaletteFormat::Normal,
|
PaletteFormat::Normal,
|
||||||
16
|
16,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let save_path = tmp_dir.path().join("test_save_normal_format_16_colors.pal");
|
let save_path = tmp_dir.path().join("test_save_normal_format_16_colors.pal");
|
||||||
|
|
|
@ -1,29 +1,21 @@
|
||||||
pub use crate::graphics::{
|
pub use crate::graphics::{
|
||||||
*,
|
//
|
||||||
bitmap::{
|
bitmap::{
|
||||||
*,
|
|
||||||
blit::*,
|
blit::*,
|
||||||
general::*,
|
general::*,
|
||||||
gif::*,
|
gif::*,
|
||||||
iff::*,
|
iff::*,
|
||||||
indexed::{
|
indexed::{blit::*, primitives::*, *},
|
||||||
*,
|
|
||||||
blit::*,
|
|
||||||
primitives::*,
|
|
||||||
},
|
|
||||||
pcx::*,
|
pcx::*,
|
||||||
png::*,
|
png::*,
|
||||||
primitives::*,
|
primitives::*,
|
||||||
rgb::{
|
rgb::{blit::*, primitives::*, *},
|
||||||
*,
|
*,
|
||||||
blit::*,
|
|
||||||
primitives::*,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
bitmapatlas::*,
|
bitmapatlas::*,
|
||||||
blendmap::*,
|
blendmap::*,
|
||||||
color::*,
|
color::*,
|
||||||
font::*,
|
font::*,
|
||||||
palette::*,
|
palette::*,
|
||||||
|
*,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -53,4 +53,4 @@ pub mod tests {
|
||||||
}
|
}
|
||||||
Ok(buffer.into_boxed_slice())
|
Ok(buffer.into_boxed_slice())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::math::{distance_between, distance_squared_between};
|
|
||||||
use crate::math::vector2::Vector2;
|
use crate::math::vector2::Vector2;
|
||||||
|
use crate::math::{distance_between, distance_squared_between};
|
||||||
|
|
||||||
/// Represents a 2D circle, using integer center coordinates and radius.
|
/// Represents a 2D circle, using integer center coordinates and radius.
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
@ -38,7 +38,7 @@ impl Circle {
|
||||||
let center_y = (max_y - min_y) / 2.0;
|
let center_y = (max_y - min_y) / 2.0;
|
||||||
|
|
||||||
Some(Circle {
|
Some(Circle {
|
||||||
x: center_x as i32,
|
x: center_x as i32, //
|
||||||
y: center_y as i32,
|
y: center_y as i32,
|
||||||
radius: radius as u32,
|
radius: radius as u32,
|
||||||
})
|
})
|
||||||
|
@ -52,18 +52,15 @@ impl Circle {
|
||||||
|
|
||||||
/// Returns true if the given point is contained within the bounds of this circle.
|
/// Returns true if the given point is contained within the bounds of this circle.
|
||||||
pub fn contains_point(&self, x: i32, y: i32) -> bool {
|
pub fn contains_point(&self, x: i32, y: i32) -> bool {
|
||||||
let distance_squared =
|
let distance_squared = distance_squared_between(self.x as f32, self.y as f32, x as f32, y as f32);
|
||||||
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;
|
let radius_squared = (self.radius * self.radius) as f32;
|
||||||
distance_squared <= radius_squared
|
distance_squared <= radius_squared
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if the given circle at least partially overlaps the bounds of this circle.
|
/// Returns true if the given circle at least partially overlaps the bounds of this circle.
|
||||||
pub fn overlaps(&self, other: &Circle) -> bool {
|
pub fn overlaps(&self, other: &Circle) -> bool {
|
||||||
let distance_squared =
|
let distance_squared = distance_squared_between(self.x as f32, self.y as f32, other.x as f32, other.y as f32);
|
||||||
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;
|
||||||
let minimum_distance_squared =
|
|
||||||
((self.radius + other.radius) * (self.radius + other.radius)) as f32;
|
|
||||||
distance_squared <= minimum_distance_squared
|
distance_squared <= minimum_distance_squared
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,8 +20,13 @@ impl Matrix3x3 {
|
||||||
pub const M32: usize = 5;
|
pub const M32: usize = 5;
|
||||||
pub const M33: usize = 8;
|
pub const M33: usize = 8;
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
pub const IDENTITY: Matrix3x3 = Matrix3x3 {
|
pub const IDENTITY: Matrix3x3 = Matrix3x3 {
|
||||||
m: [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0],
|
m: [
|
||||||
|
1.0, 0.0, 0.0,
|
||||||
|
0.0, 1.0, 0.0,
|
||||||
|
0.0, 0.0, 1.0
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Returns a new identity matrix.
|
/// Returns a new identity matrix.
|
||||||
|
@ -208,18 +213,7 @@ impl Matrix3x3 {
|
||||||
|
|
||||||
/// Sets all of the elements of this matrix.
|
/// Sets all of the elements of this matrix.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set(
|
pub fn set(&mut self, m11: f32, m12: f32, m13: f32, m21: f32, m22: f32, m23: f32, m31: f32, m32: f32, m33: f32) {
|
||||||
&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::M11] = m11;
|
||||||
self.m[Matrix3x3::M12] = m12;
|
self.m[Matrix3x3::M12] = m12;
|
||||||
self.m[Matrix3x3::M13] = m13;
|
self.m[Matrix3x3::M13] = m13;
|
||||||
|
|
|
@ -53,8 +53,8 @@ pub fn nearly_equal(a: f32, b: f32, epsilon: f32) -> bool {
|
||||||
/// * `t`: the amount to interpolate between the two values, specified as a fraction
|
/// * `t`: the amount to interpolate between the two values, specified as a fraction
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn lerp<N>(a: N, b: N, t: f32) -> N
|
pub fn lerp<N>(a: N, b: N, t: f32) -> N
|
||||||
where
|
where
|
||||||
N: Copy + Add<Output=N> + Sub<Output=N> + Mul<f32, Output=N>,
|
N: Copy + Add<Output = N> + Sub<Output = N> + Mul<f32, Output = N>,
|
||||||
{
|
{
|
||||||
a + (b - a) * t
|
a + (b - a) * t
|
||||||
}
|
}
|
||||||
|
@ -69,8 +69,8 @@ pub fn lerp<N>(a: N, b: N, t: f32) -> N
|
||||||
/// * `lerp_result`: the interpolated value between the range given
|
/// * `lerp_result`: the interpolated value between the range given
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn inverse_lerp<N>(a: N, b: N, lerp_result: N) -> f32
|
pub fn inverse_lerp<N>(a: N, b: N, lerp_result: N) -> f32
|
||||||
where
|
where
|
||||||
N: Copy + Sub<Output=N> + Div<N, Output=f32>,
|
N: Copy + Sub<Output = N> + Div<N, Output = f32>,
|
||||||
{
|
{
|
||||||
(lerp_result - a) / (b - a)
|
(lerp_result - a) / (b - a)
|
||||||
}
|
}
|
||||||
|
@ -84,8 +84,8 @@ pub fn inverse_lerp<N>(a: N, b: N, lerp_result: N) -> f32
|
||||||
/// * `t`: the amount to interpolate between the two values, specified as a fraction
|
/// * `t`: the amount to interpolate between the two values, specified as a fraction
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn smooth_lerp<N>(a: N, b: N, t: f32) -> N
|
pub fn smooth_lerp<N>(a: N, b: N, t: f32) -> N
|
||||||
where
|
where
|
||||||
N: Copy + Add<Output=N> + Sub<Output=N> + Mul<f32, Output=N>,
|
N: Copy + Add<Output = N> + Sub<Output = N> + Mul<f32, Output = N>,
|
||||||
{
|
{
|
||||||
let t = t.clamp(0.0, 1.0);
|
let t = t.clamp(0.0, 1.0);
|
||||||
lerp(a, b, (t * t) * (3.0 - (2.0 * t)))
|
lerp(a, b, (t * t) * (3.0 - (2.0 * t)))
|
||||||
|
@ -104,8 +104,8 @@ pub fn smooth_lerp<N>(a: N, b: N, t: f32) -> N
|
||||||
/// * `new_max`: new max value (high end of range)
|
/// * `new_max`: new max value (high end of range)
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn scale_range<N>(value: N, old_min: N, old_max: N, new_min: N, new_max: N) -> N
|
pub fn scale_range<N>(value: N, old_min: N, old_max: N, new_min: N, new_max: N) -> N
|
||||||
where
|
where
|
||||||
N: Copy + Add<Output=N> + Sub<Output=N> + Mul<Output=N> + Div<Output=N>,
|
N: Copy + Add<Output = N> + Sub<Output = N> + Mul<Output = N> + Div<Output = N>,
|
||||||
{
|
{
|
||||||
(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
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
pub use crate::math::{
|
pub use crate::math::{
|
||||||
*,
|
//
|
||||||
circle::*,
|
circle::*,
|
||||||
matrix3x3::*,
|
matrix3x3::*,
|
||||||
rect::*,
|
rect::*,
|
||||||
vector2::*,
|
vector2::*,
|
||||||
|
*,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -10,12 +10,7 @@ pub struct Rect {
|
||||||
impl Rect {
|
impl Rect {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(x: i32, y: i32, width: u32, height: u32) -> Rect {
|
pub fn new(x: i32, y: i32, width: u32, height: u32) -> Rect {
|
||||||
Rect {
|
Rect { x, y, width, height }
|
||||||
x,
|
|
||||||
y,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new rect from the specified coordinates. Automatically determines if the
|
/// Creates a new rect from the specified coordinates. Automatically determines if the
|
||||||
|
@ -51,7 +46,7 @@ impl Rect {
|
||||||
}
|
}
|
||||||
|
|
||||||
Rect {
|
Rect {
|
||||||
x,
|
x, //
|
||||||
y,
|
y,
|
||||||
width: width as u32,
|
width: width as u32,
|
||||||
height: height as u32,
|
height: height as u32,
|
||||||
|
@ -165,21 +160,11 @@ pub mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn right_and_left() {
|
pub fn right_and_left() {
|
||||||
let rect = Rect {
|
let rect = Rect { x: 5, y: 6, width: 16, height: 12 };
|
||||||
x: 5,
|
|
||||||
y: 6,
|
|
||||||
width: 16,
|
|
||||||
height: 12,
|
|
||||||
};
|
|
||||||
assert_eq!(20, rect.right());
|
assert_eq!(20, rect.right());
|
||||||
assert_eq!(17, rect.bottom());
|
assert_eq!(17, rect.bottom());
|
||||||
|
|
||||||
let rect = Rect {
|
let rect = Rect { x: -11, y: -25, width: 16, height: 12 };
|
||||||
x: -11,
|
|
||||||
y: -25,
|
|
||||||
width: 16,
|
|
||||||
height: 12,
|
|
||||||
};
|
|
||||||
assert_eq!(4, rect.right());
|
assert_eq!(4, rect.right());
|
||||||
assert_eq!(-14, rect.bottom());
|
assert_eq!(-14, rect.bottom());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
||||||
|
|
||||||
use crate::math::{angle_between, angle_to_direction, nearly_equal, NearlyEqual};
|
|
||||||
use crate::math::matrix3x3::Matrix3x3;
|
use crate::math::matrix3x3::Matrix3x3;
|
||||||
|
use crate::math::{angle_between, angle_to_direction, nearly_equal, NearlyEqual};
|
||||||
|
|
||||||
/// Represents a 2D vector and provides common methods for vector math.
|
/// Represents a 2D vector and provides common methods for vector math.
|
||||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
|
@ -68,7 +68,7 @@ impl Vector2 {
|
||||||
pub fn normalize(&self) -> Vector2 {
|
pub fn normalize(&self) -> Vector2 {
|
||||||
let inverse_length = 1.0 / self.length();
|
let inverse_length = 1.0 / self.length();
|
||||||
Vector2 {
|
Vector2 {
|
||||||
x: self.x * inverse_length,
|
x: self.x * inverse_length, //
|
||||||
y: self.y * inverse_length,
|
y: self.y * inverse_length,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -104,7 +104,7 @@ impl Neg for Vector2 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn neg(self) -> Self::Output {
|
fn neg(self) -> Self::Output {
|
||||||
Vector2 {
|
Vector2 {
|
||||||
x: -self.x,
|
x: -self.x, //
|
||||||
y: -self.y,
|
y: -self.y,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,7 +116,7 @@ impl Add for Vector2 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn add(self, rhs: Self) -> Self::Output {
|
fn add(self, rhs: Self) -> Self::Output {
|
||||||
Vector2 {
|
Vector2 {
|
||||||
x: self.x + rhs.x,
|
x: self.x + rhs.x, //
|
||||||
y: self.y + rhs.y,
|
y: self.y + rhs.y,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -136,7 +136,7 @@ impl Sub for Vector2 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn sub(self, rhs: Self) -> Self::Output {
|
fn sub(self, rhs: Self) -> Self::Output {
|
||||||
Vector2 {
|
Vector2 {
|
||||||
x: self.x - rhs.x,
|
x: self.x - rhs.x, //
|
||||||
y: self.y - rhs.y,
|
y: self.y - rhs.y,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,7 +156,7 @@ impl Mul for Vector2 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn mul(self, rhs: Self) -> Self::Output {
|
fn mul(self, rhs: Self) -> Self::Output {
|
||||||
Vector2 {
|
Vector2 {
|
||||||
x: self.x * rhs.x,
|
x: self.x * rhs.x, //
|
||||||
y: self.y * rhs.y,
|
y: self.y * rhs.y,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -176,7 +176,7 @@ impl Div for Vector2 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn div(self, rhs: Self) -> Self::Output {
|
fn div(self, rhs: Self) -> Self::Output {
|
||||||
Vector2 {
|
Vector2 {
|
||||||
x: self.x / rhs.x,
|
x: self.x / rhs.x, //
|
||||||
y: self.y / rhs.y,
|
y: self.y / rhs.y,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -196,7 +196,7 @@ impl Mul<f32> for Vector2 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn mul(self, rhs: f32) -> Self::Output {
|
fn mul(self, rhs: f32) -> Self::Output {
|
||||||
Vector2 {
|
Vector2 {
|
||||||
x: self.x * rhs,
|
x: self.x * rhs, //
|
||||||
y: self.y * rhs,
|
y: self.y * rhs,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -216,7 +216,7 @@ impl Div<f32> for Vector2 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn div(self, rhs: f32) -> Self::Output {
|
fn div(self, rhs: f32) -> Self::Output {
|
||||||
Vector2 {
|
Vector2 {
|
||||||
x: self.x / rhs,
|
x: self.x / rhs, //
|
||||||
y: self.y / rhs,
|
y: self.y / rhs,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
*,
|
//
|
||||||
audio::prelude::*,
|
audio::prelude::*,
|
||||||
base::*,
|
base::*,
|
||||||
entities::*,
|
entities::*,
|
||||||
|
@ -9,10 +9,8 @@ pub use crate::{
|
||||||
states::*,
|
states::*,
|
||||||
system::{
|
system::{
|
||||||
prelude::*,
|
prelude::*,
|
||||||
res::{
|
res::{dos_like::*, standard::*},
|
||||||
dos_like::*,
|
|
||||||
standard::*,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
utils::prelude::*,
|
utils::prelude::*,
|
||||||
|
*,
|
||||||
};
|
};
|
||||||
|
|
|
@ -81,7 +81,7 @@ impl<ContextType> std::fmt::Debug for StateContainer<ContextType> {
|
||||||
impl<ContextType> StateContainer<ContextType> {
|
impl<ContextType> StateContainer<ContextType> {
|
||||||
pub fn new(state: Box<dyn AppState<ContextType>>) -> Self {
|
pub fn new(state: Box<dyn AppState<ContextType>>) -> Self {
|
||||||
StateContainer {
|
StateContainer {
|
||||||
current_state: State::Dead,
|
current_state: State::Dead, //
|
||||||
pending_state_change: None,
|
pending_state_change: None,
|
||||||
state,
|
state,
|
||||||
}
|
}
|
||||||
|
@ -133,9 +133,7 @@ impl<ContextType> StateContainer<ContextType> {
|
||||||
self.change_state(State::TransitionIn, context);
|
self.change_state(State::TransitionIn, context);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => {
|
_ => Err(StateError::AppStateInvalidState(self.current_state)),
|
||||||
Err(StateError::AppStateInvalidState(self.current_state))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,13 +183,16 @@ pub struct States<ContextType> {
|
||||||
|
|
||||||
impl<ContextType> std::fmt::Debug for States<ContextType> {
|
impl<ContextType> std::fmt::Debug for States<ContextType> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
f.debug_struct("States")
|
f.debug_struct("States") //
|
||||||
.field("states", &self.states)
|
.field("states", &self.states)
|
||||||
.field("command", &self.command)
|
.field("command", &self.command)
|
||||||
.field("pending_state", match self.pending_state {
|
.field(
|
||||||
Some(..) => &"Some(..)",
|
"pending_state",
|
||||||
None => &"None",
|
match self.pending_state {
|
||||||
})
|
Some(..) => &"Some(..)",
|
||||||
|
None => &"None",
|
||||||
|
},
|
||||||
|
)
|
||||||
.field("pop_count", &self.pop_count)
|
.field("pop_count", &self.pop_count)
|
||||||
.finish_non_exhaustive()
|
.finish_non_exhaustive()
|
||||||
}
|
}
|
||||||
|
@ -200,7 +201,7 @@ impl<ContextType> std::fmt::Debug for States<ContextType> {
|
||||||
impl<ContextType> States<ContextType> {
|
impl<ContextType> States<ContextType> {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
States {
|
States {
|
||||||
states: VecDeque::new(),
|
states: VecDeque::new(), //
|
||||||
command: None,
|
command: None,
|
||||||
pending_state: None,
|
pending_state: None,
|
||||||
pop_count: None,
|
pop_count: None,
|
||||||
|
@ -400,8 +401,12 @@ impl<ContextType> States<ContextType> {
|
||||||
// state has indicated it is done transitioning, so we can switch it to whatever
|
// state has indicated it is done transitioning, so we can switch it to whatever
|
||||||
// it was transitioning to
|
// it was transitioning to
|
||||||
match to {
|
match to {
|
||||||
TransitionTo::Paused => { state.pending_pause(); }
|
TransitionTo::Paused => {
|
||||||
TransitionTo::Dead => { state.pending_kill(); }
|
state.pending_pause();
|
||||||
|
}
|
||||||
|
TransitionTo::Dead => {
|
||||||
|
state.pending_kill();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -444,7 +449,6 @@ impl<ContextType> States<ContextType> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use claim::*;
|
use claim::*;
|
||||||
|
@ -465,9 +469,7 @@ mod tests {
|
||||||
|
|
||||||
impl TestContext {
|
impl TestContext {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
TestContext {
|
TestContext { log: Vec::new() }
|
||||||
log: Vec::new()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn log(&mut self, entry: LogEntry) {
|
pub fn log(&mut self, entry: LogEntry) {
|
||||||
|
@ -490,7 +492,7 @@ mod tests {
|
||||||
impl TestState {
|
impl TestState {
|
||||||
pub fn new(id: u32) -> Self {
|
pub fn new(id: u32) -> Self {
|
||||||
TestState {
|
TestState {
|
||||||
id,
|
id, //
|
||||||
counter: 0,
|
counter: 0,
|
||||||
transition_length: 0,
|
transition_length: 0,
|
||||||
}
|
}
|
||||||
|
@ -498,7 +500,7 @@ mod tests {
|
||||||
|
|
||||||
pub fn new_with_transition_length(id: u32, transition_length: u32) -> Self {
|
pub fn new_with_transition_length(id: u32, transition_length: u32) -> Self {
|
||||||
TestState {
|
TestState {
|
||||||
id,
|
id, //
|
||||||
counter: 0,
|
counter: 0,
|
||||||
transition_length,
|
transition_length,
|
||||||
}
|
}
|
||||||
|
@ -562,7 +564,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(FOO, Pending, Dead),
|
StateChange(FOO, Pending, Dead), //
|
||||||
StateChange(FOO, TransitionIn, Pending),
|
StateChange(FOO, TransitionIn, Pending),
|
||||||
Transition(FOO, TransitionIn),
|
Transition(FOO, TransitionIn),
|
||||||
Update(FOO, TransitionIn),
|
Update(FOO, TransitionIn),
|
||||||
|
@ -574,7 +576,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(FOO, Active, TransitionIn),
|
StateChange(FOO, Active, TransitionIn), //
|
||||||
Update(FOO, Active),
|
Update(FOO, Active),
|
||||||
Render(FOO, Active),
|
Render(FOO, Active),
|
||||||
]
|
]
|
||||||
|
@ -588,7 +590,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(FOO, TransitionOut(TransitionTo::Dead), Active),
|
StateChange(FOO, TransitionOut(TransitionTo::Dead), Active), //
|
||||||
Transition(FOO, TransitionOut(TransitionTo::Dead)),
|
Transition(FOO, TransitionOut(TransitionTo::Dead)),
|
||||||
Update(FOO, TransitionOut(TransitionTo::Dead)),
|
Update(FOO, TransitionOut(TransitionTo::Dead)),
|
||||||
Render(FOO, TransitionOut(TransitionTo::Dead)),
|
Render(FOO, TransitionOut(TransitionTo::Dead)),
|
||||||
|
@ -623,7 +625,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(FOO, Pending, Dead),
|
StateChange(FOO, Pending, Dead), //
|
||||||
StateChange(FOO, TransitionIn, Pending),
|
StateChange(FOO, TransitionIn, Pending),
|
||||||
Transition(FOO, TransitionIn),
|
Transition(FOO, TransitionIn),
|
||||||
Update(FOO, TransitionIn),
|
Update(FOO, TransitionIn),
|
||||||
|
@ -636,7 +638,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
Transition(FOO, TransitionIn),
|
Transition(FOO, TransitionIn), //
|
||||||
Update(FOO, TransitionIn),
|
Update(FOO, TransitionIn),
|
||||||
Render(FOO, TransitionIn),
|
Render(FOO, TransitionIn),
|
||||||
]
|
]
|
||||||
|
@ -647,7 +649,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(FOO, Active, TransitionIn),
|
StateChange(FOO, Active, TransitionIn), //
|
||||||
Update(FOO, Active),
|
Update(FOO, Active),
|
||||||
Render(FOO, Active),
|
Render(FOO, Active),
|
||||||
]
|
]
|
||||||
|
@ -661,7 +663,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(FOO, TransitionOut(TransitionTo::Dead), Active),
|
StateChange(FOO, TransitionOut(TransitionTo::Dead), Active), //
|
||||||
Transition(FOO, TransitionOut(TransitionTo::Dead)),
|
Transition(FOO, TransitionOut(TransitionTo::Dead)),
|
||||||
Update(FOO, TransitionOut(TransitionTo::Dead)),
|
Update(FOO, TransitionOut(TransitionTo::Dead)),
|
||||||
Render(FOO, TransitionOut(TransitionTo::Dead)),
|
Render(FOO, TransitionOut(TransitionTo::Dead)),
|
||||||
|
@ -673,7 +675,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
Transition(FOO, TransitionOut(TransitionTo::Dead)),
|
Transition(FOO, TransitionOut(TransitionTo::Dead)), //
|
||||||
Update(FOO, TransitionOut(TransitionTo::Dead)),
|
Update(FOO, TransitionOut(TransitionTo::Dead)),
|
||||||
Render(FOO, TransitionOut(TransitionTo::Dead)),
|
Render(FOO, TransitionOut(TransitionTo::Dead)),
|
||||||
]
|
]
|
||||||
|
@ -710,7 +712,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(FIRST, Pending, Dead),
|
StateChange(FIRST, Pending, Dead), //
|
||||||
StateChange(FIRST, TransitionIn, Pending),
|
StateChange(FIRST, TransitionIn, Pending),
|
||||||
Transition(FIRST, TransitionIn),
|
Transition(FIRST, TransitionIn),
|
||||||
Update(FIRST, TransitionIn),
|
Update(FIRST, TransitionIn),
|
||||||
|
@ -722,7 +724,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(FIRST, Active, TransitionIn),
|
StateChange(FIRST, Active, TransitionIn), //
|
||||||
Update(FIRST, Active),
|
Update(FIRST, Active),
|
||||||
Render(FIRST, Active),
|
Render(FIRST, Active),
|
||||||
]
|
]
|
||||||
|
@ -737,7 +739,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(FIRST, TransitionOut(TransitionTo::Paused), Active),
|
StateChange(FIRST, TransitionOut(TransitionTo::Paused), Active), //
|
||||||
Transition(FIRST, TransitionOut(TransitionTo::Paused)),
|
Transition(FIRST, TransitionOut(TransitionTo::Paused)),
|
||||||
Update(FIRST, TransitionOut(TransitionTo::Paused)),
|
Update(FIRST, TransitionOut(TransitionTo::Paused)),
|
||||||
Render(FIRST, TransitionOut(TransitionTo::Paused)),
|
Render(FIRST, TransitionOut(TransitionTo::Paused)),
|
||||||
|
@ -749,7 +751,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(FIRST, Paused, TransitionOut(TransitionTo::Paused)),
|
StateChange(FIRST, Paused, TransitionOut(TransitionTo::Paused)), //
|
||||||
StateChange(SECOND, Pending, Dead),
|
StateChange(SECOND, Pending, Dead),
|
||||||
StateChange(SECOND, TransitionIn, Pending),
|
StateChange(SECOND, TransitionIn, Pending),
|
||||||
Transition(SECOND, TransitionIn),
|
Transition(SECOND, TransitionIn),
|
||||||
|
@ -762,7 +764,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(SECOND, Active, TransitionIn),
|
StateChange(SECOND, Active, TransitionIn), //
|
||||||
Update(SECOND, Active),
|
Update(SECOND, Active),
|
||||||
Render(SECOND, Active),
|
Render(SECOND, Active),
|
||||||
]
|
]
|
||||||
|
@ -777,7 +779,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(SECOND, TransitionOut(TransitionTo::Dead), Active),
|
StateChange(SECOND, TransitionOut(TransitionTo::Dead), Active), //
|
||||||
Transition(SECOND, TransitionOut(TransitionTo::Dead)),
|
Transition(SECOND, TransitionOut(TransitionTo::Dead)),
|
||||||
Update(SECOND, TransitionOut(TransitionTo::Dead)),
|
Update(SECOND, TransitionOut(TransitionTo::Dead)),
|
||||||
Render(SECOND, TransitionOut(TransitionTo::Dead)),
|
Render(SECOND, TransitionOut(TransitionTo::Dead)),
|
||||||
|
@ -789,7 +791,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(SECOND, Dead, TransitionOut(TransitionTo::Dead)),
|
StateChange(SECOND, Dead, TransitionOut(TransitionTo::Dead)), //
|
||||||
StateChange(FIRST, Resume, Paused),
|
StateChange(FIRST, Resume, Paused),
|
||||||
StateChange(FIRST, TransitionIn, Resume),
|
StateChange(FIRST, TransitionIn, Resume),
|
||||||
Transition(FIRST, TransitionIn),
|
Transition(FIRST, TransitionIn),
|
||||||
|
@ -802,7 +804,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(FIRST, Active, TransitionIn),
|
StateChange(FIRST, Active, TransitionIn), //
|
||||||
Update(FIRST, Active),
|
Update(FIRST, Active),
|
||||||
Render(FIRST, Active),
|
Render(FIRST, Active),
|
||||||
]
|
]
|
||||||
|
@ -817,7 +819,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(FIRST, TransitionOut(TransitionTo::Dead), Active),
|
StateChange(FIRST, TransitionOut(TransitionTo::Dead), Active), //
|
||||||
Transition(FIRST, TransitionOut(TransitionTo::Dead)),
|
Transition(FIRST, TransitionOut(TransitionTo::Dead)),
|
||||||
Update(FIRST, TransitionOut(TransitionTo::Dead)),
|
Update(FIRST, TransitionOut(TransitionTo::Dead)),
|
||||||
Render(FIRST, TransitionOut(TransitionTo::Dead)),
|
Render(FIRST, TransitionOut(TransitionTo::Dead)),
|
||||||
|
@ -854,7 +856,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(FIRST, Pending, Dead),
|
StateChange(FIRST, Pending, Dead), //
|
||||||
StateChange(FIRST, TransitionIn, Pending),
|
StateChange(FIRST, TransitionIn, Pending),
|
||||||
Transition(FIRST, TransitionIn),
|
Transition(FIRST, TransitionIn),
|
||||||
Update(FIRST, TransitionIn),
|
Update(FIRST, TransitionIn),
|
||||||
|
@ -867,7 +869,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
Transition(FIRST, TransitionIn),
|
Transition(FIRST, TransitionIn), //
|
||||||
Update(FIRST, TransitionIn),
|
Update(FIRST, TransitionIn),
|
||||||
Render(FIRST, TransitionIn),
|
Render(FIRST, TransitionIn),
|
||||||
]
|
]
|
||||||
|
@ -878,7 +880,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(FIRST, Active, TransitionIn),
|
StateChange(FIRST, Active, TransitionIn), //
|
||||||
Update(FIRST, Active),
|
Update(FIRST, Active),
|
||||||
Render(FIRST, Active),
|
Render(FIRST, Active),
|
||||||
]
|
]
|
||||||
|
@ -894,7 +896,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(FIRST, TransitionOut(TransitionTo::Paused), Active),
|
StateChange(FIRST, TransitionOut(TransitionTo::Paused), Active), //
|
||||||
Transition(FIRST, TransitionOut(TransitionTo::Paused)),
|
Transition(FIRST, TransitionOut(TransitionTo::Paused)),
|
||||||
Update(FIRST, TransitionOut(TransitionTo::Paused)),
|
Update(FIRST, TransitionOut(TransitionTo::Paused)),
|
||||||
Render(FIRST, TransitionOut(TransitionTo::Paused)),
|
Render(FIRST, TransitionOut(TransitionTo::Paused)),
|
||||||
|
@ -906,7 +908,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
Transition(FIRST, TransitionOut(TransitionTo::Paused)),
|
Transition(FIRST, TransitionOut(TransitionTo::Paused)), //
|
||||||
Update(FIRST, TransitionOut(TransitionTo::Paused)),
|
Update(FIRST, TransitionOut(TransitionTo::Paused)),
|
||||||
Render(FIRST, TransitionOut(TransitionTo::Paused)),
|
Render(FIRST, TransitionOut(TransitionTo::Paused)),
|
||||||
]
|
]
|
||||||
|
@ -917,7 +919,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(FIRST, Paused, TransitionOut(TransitionTo::Paused)),
|
StateChange(FIRST, Paused, TransitionOut(TransitionTo::Paused)), //
|
||||||
StateChange(SECOND, Pending, Dead),
|
StateChange(SECOND, Pending, Dead),
|
||||||
StateChange(SECOND, TransitionIn, Pending),
|
StateChange(SECOND, TransitionIn, Pending),
|
||||||
Transition(SECOND, TransitionIn),
|
Transition(SECOND, TransitionIn),
|
||||||
|
@ -931,7 +933,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
Transition(SECOND, TransitionIn),
|
Transition(SECOND, TransitionIn), //
|
||||||
Update(SECOND, TransitionIn),
|
Update(SECOND, TransitionIn),
|
||||||
Render(SECOND, TransitionIn),
|
Render(SECOND, TransitionIn),
|
||||||
]
|
]
|
||||||
|
@ -942,7 +944,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(SECOND, Active, TransitionIn),
|
StateChange(SECOND, Active, TransitionIn), //
|
||||||
Update(SECOND, Active),
|
Update(SECOND, Active),
|
||||||
Render(SECOND, Active),
|
Render(SECOND, Active),
|
||||||
]
|
]
|
||||||
|
@ -957,7 +959,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(SECOND, TransitionOut(TransitionTo::Dead), Active),
|
StateChange(SECOND, TransitionOut(TransitionTo::Dead), Active), //
|
||||||
Transition(SECOND, TransitionOut(TransitionTo::Dead)),
|
Transition(SECOND, TransitionOut(TransitionTo::Dead)),
|
||||||
Update(SECOND, TransitionOut(TransitionTo::Dead)),
|
Update(SECOND, TransitionOut(TransitionTo::Dead)),
|
||||||
Render(SECOND, TransitionOut(TransitionTo::Dead)),
|
Render(SECOND, TransitionOut(TransitionTo::Dead)),
|
||||||
|
@ -969,7 +971,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
Transition(SECOND, TransitionOut(TransitionTo::Dead)),
|
Transition(SECOND, TransitionOut(TransitionTo::Dead)), //
|
||||||
Update(SECOND, TransitionOut(TransitionTo::Dead)),
|
Update(SECOND, TransitionOut(TransitionTo::Dead)),
|
||||||
Render(SECOND, TransitionOut(TransitionTo::Dead)),
|
Render(SECOND, TransitionOut(TransitionTo::Dead)),
|
||||||
]
|
]
|
||||||
|
@ -981,7 +983,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(SECOND, Dead, TransitionOut(TransitionTo::Dead)),
|
StateChange(SECOND, Dead, TransitionOut(TransitionTo::Dead)), //
|
||||||
StateChange(FIRST, Resume, Paused),
|
StateChange(FIRST, Resume, Paused),
|
||||||
StateChange(FIRST, TransitionIn, Resume),
|
StateChange(FIRST, TransitionIn, Resume),
|
||||||
Transition(FIRST, TransitionIn),
|
Transition(FIRST, TransitionIn),
|
||||||
|
@ -995,7 +997,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
Transition(FIRST, TransitionIn),
|
Transition(FIRST, TransitionIn), //
|
||||||
Update(FIRST, TransitionIn),
|
Update(FIRST, TransitionIn),
|
||||||
Render(FIRST, TransitionIn),
|
Render(FIRST, TransitionIn),
|
||||||
]
|
]
|
||||||
|
@ -1006,7 +1008,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(FIRST, Active, TransitionIn),
|
StateChange(FIRST, Active, TransitionIn), //
|
||||||
Update(FIRST, Active),
|
Update(FIRST, Active),
|
||||||
Render(FIRST, Active),
|
Render(FIRST, Active),
|
||||||
]
|
]
|
||||||
|
@ -1021,7 +1023,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(FIRST, TransitionOut(TransitionTo::Dead), Active),
|
StateChange(FIRST, TransitionOut(TransitionTo::Dead), Active), //
|
||||||
Transition(FIRST, TransitionOut(TransitionTo::Dead)),
|
Transition(FIRST, TransitionOut(TransitionTo::Dead)),
|
||||||
Update(FIRST, TransitionOut(TransitionTo::Dead)),
|
Update(FIRST, TransitionOut(TransitionTo::Dead)),
|
||||||
Render(FIRST, TransitionOut(TransitionTo::Dead)),
|
Render(FIRST, TransitionOut(TransitionTo::Dead)),
|
||||||
|
@ -1033,7 +1035,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
Transition(FIRST, TransitionOut(TransitionTo::Dead)),
|
Transition(FIRST, TransitionOut(TransitionTo::Dead)), //
|
||||||
Update(FIRST, TransitionOut(TransitionTo::Dead)),
|
Update(FIRST, TransitionOut(TransitionTo::Dead)),
|
||||||
Render(FIRST, TransitionOut(TransitionTo::Dead)),
|
Render(FIRST, TransitionOut(TransitionTo::Dead)),
|
||||||
]
|
]
|
||||||
|
@ -1070,7 +1072,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(FIRST, Pending, Dead),
|
StateChange(FIRST, Pending, Dead), //
|
||||||
StateChange(FIRST, TransitionIn, Pending),
|
StateChange(FIRST, TransitionIn, Pending),
|
||||||
Transition(FIRST, TransitionIn),
|
Transition(FIRST, TransitionIn),
|
||||||
Update(FIRST, TransitionIn),
|
Update(FIRST, TransitionIn),
|
||||||
|
@ -1082,7 +1084,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(FIRST, Active, TransitionIn),
|
StateChange(FIRST, Active, TransitionIn), //
|
||||||
Update(FIRST, Active),
|
Update(FIRST, Active),
|
||||||
Render(FIRST, Active),
|
Render(FIRST, Active),
|
||||||
]
|
]
|
||||||
|
@ -1097,7 +1099,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(FIRST, TransitionOut(TransitionTo::Paused), Active),
|
StateChange(FIRST, TransitionOut(TransitionTo::Paused), Active), //
|
||||||
Transition(FIRST, TransitionOut(TransitionTo::Paused)),
|
Transition(FIRST, TransitionOut(TransitionTo::Paused)),
|
||||||
Update(FIRST, TransitionOut(TransitionTo::Paused)),
|
Update(FIRST, TransitionOut(TransitionTo::Paused)),
|
||||||
Render(FIRST, TransitionOut(TransitionTo::Paused)),
|
Render(FIRST, TransitionOut(TransitionTo::Paused)),
|
||||||
|
@ -1109,7 +1111,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(FIRST, Paused, TransitionOut(TransitionTo::Paused)),
|
StateChange(FIRST, Paused, TransitionOut(TransitionTo::Paused)), //
|
||||||
StateChange(SECOND, Pending, Dead),
|
StateChange(SECOND, Pending, Dead),
|
||||||
StateChange(SECOND, TransitionIn, Pending),
|
StateChange(SECOND, TransitionIn, Pending),
|
||||||
Transition(SECOND, TransitionIn),
|
Transition(SECOND, TransitionIn),
|
||||||
|
@ -1122,7 +1124,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(SECOND, Active, TransitionIn),
|
StateChange(SECOND, Active, TransitionIn), //
|
||||||
Update(SECOND, Active),
|
Update(SECOND, Active),
|
||||||
Render(SECOND, Active),
|
Render(SECOND, Active),
|
||||||
]
|
]
|
||||||
|
@ -1137,7 +1139,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(SECOND, TransitionOut(TransitionTo::Dead), Active),
|
StateChange(SECOND, TransitionOut(TransitionTo::Dead), Active), //
|
||||||
Transition(SECOND, TransitionOut(TransitionTo::Dead)),
|
Transition(SECOND, TransitionOut(TransitionTo::Dead)),
|
||||||
Update(SECOND, TransitionOut(TransitionTo::Dead)),
|
Update(SECOND, TransitionOut(TransitionTo::Dead)),
|
||||||
Render(SECOND, TransitionOut(TransitionTo::Dead)),
|
Render(SECOND, TransitionOut(TransitionTo::Dead)),
|
||||||
|
@ -1149,7 +1151,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(SECOND, Dead, TransitionOut(TransitionTo::Dead)),
|
StateChange(SECOND, Dead, TransitionOut(TransitionTo::Dead)), //
|
||||||
StateChange(FIRST, Dead, Paused),
|
StateChange(FIRST, Dead, Paused),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
@ -1161,7 +1163,6 @@ mod tests {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn swap_states() -> Result<(), StateError> {
|
fn swap_states() -> Result<(), StateError> {
|
||||||
use LogEntry::*;
|
use LogEntry::*;
|
||||||
|
@ -1182,7 +1183,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(FIRST, Pending, Dead),
|
StateChange(FIRST, Pending, Dead), //
|
||||||
StateChange(FIRST, TransitionIn, Pending),
|
StateChange(FIRST, TransitionIn, Pending),
|
||||||
Transition(FIRST, TransitionIn),
|
Transition(FIRST, TransitionIn),
|
||||||
Update(FIRST, TransitionIn),
|
Update(FIRST, TransitionIn),
|
||||||
|
@ -1194,7 +1195,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(FIRST, Active, TransitionIn),
|
StateChange(FIRST, Active, TransitionIn), //
|
||||||
Update(FIRST, Active),
|
Update(FIRST, Active),
|
||||||
Render(FIRST, Active),
|
Render(FIRST, Active),
|
||||||
]
|
]
|
||||||
|
@ -1209,7 +1210,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(FIRST, TransitionOut(TransitionTo::Dead), Active),
|
StateChange(FIRST, TransitionOut(TransitionTo::Dead), Active), //
|
||||||
Transition(FIRST, TransitionOut(TransitionTo::Dead)),
|
Transition(FIRST, TransitionOut(TransitionTo::Dead)),
|
||||||
Update(FIRST, TransitionOut(TransitionTo::Dead)),
|
Update(FIRST, TransitionOut(TransitionTo::Dead)),
|
||||||
Render(FIRST, TransitionOut(TransitionTo::Dead)),
|
Render(FIRST, TransitionOut(TransitionTo::Dead)),
|
||||||
|
@ -1221,7 +1222,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(FIRST, Dead, TransitionOut(TransitionTo::Dead)),
|
StateChange(FIRST, Dead, TransitionOut(TransitionTo::Dead)), //
|
||||||
StateChange(SECOND, Pending, Dead),
|
StateChange(SECOND, Pending, Dead),
|
||||||
StateChange(SECOND, TransitionIn, Pending),
|
StateChange(SECOND, TransitionIn, Pending),
|
||||||
Transition(SECOND, TransitionIn),
|
Transition(SECOND, TransitionIn),
|
||||||
|
@ -1234,7 +1235,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(SECOND, Active, TransitionIn),
|
StateChange(SECOND, Active, TransitionIn), //
|
||||||
Update(SECOND, Active),
|
Update(SECOND, Active),
|
||||||
Render(SECOND, Active),
|
Render(SECOND, Active),
|
||||||
]
|
]
|
||||||
|
@ -1248,7 +1249,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(SECOND, TransitionOut(TransitionTo::Dead), Active),
|
StateChange(SECOND, TransitionOut(TransitionTo::Dead), Active), //
|
||||||
Transition(SECOND, TransitionOut(TransitionTo::Dead)),
|
Transition(SECOND, TransitionOut(TransitionTo::Dead)),
|
||||||
Update(SECOND, TransitionOut(TransitionTo::Dead)),
|
Update(SECOND, TransitionOut(TransitionTo::Dead)),
|
||||||
Render(SECOND, TransitionOut(TransitionTo::Dead)),
|
Render(SECOND, TransitionOut(TransitionTo::Dead)),
|
||||||
|
@ -1275,7 +1276,7 @@ mod tests {
|
||||||
impl SelfPushPopState {
|
impl SelfPushPopState {
|
||||||
pub fn new(id: u32, push_after: Option<u32>, pop_after: u32) -> Self {
|
pub fn new(id: u32, push_after: Option<u32>, pop_after: u32) -> Self {
|
||||||
SelfPushPopState {
|
SelfPushPopState {
|
||||||
id,
|
id, //
|
||||||
counter: 0,
|
counter: 0,
|
||||||
push_after,
|
push_after,
|
||||||
pop_after,
|
pop_after,
|
||||||
|
@ -1311,7 +1312,6 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn state_can_push_and_pop_states_itself() -> Result<(), StateError> {
|
fn state_can_push_and_pop_states_itself() -> Result<(), StateError> {
|
||||||
use LogEntry::*;
|
use LogEntry::*;
|
||||||
|
@ -1332,7 +1332,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(FIRST, Pending, Dead),
|
StateChange(FIRST, Pending, Dead), //
|
||||||
StateChange(FIRST, TransitionIn, Pending),
|
StateChange(FIRST, TransitionIn, Pending),
|
||||||
Transition(FIRST, TransitionIn),
|
Transition(FIRST, TransitionIn),
|
||||||
Update(FIRST, TransitionIn),
|
Update(FIRST, TransitionIn),
|
||||||
|
@ -1344,7 +1344,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(FIRST, Active, TransitionIn),
|
StateChange(FIRST, Active, TransitionIn), //
|
||||||
Update(FIRST, Active),
|
Update(FIRST, Active),
|
||||||
Render(FIRST, Active),
|
Render(FIRST, Active),
|
||||||
]
|
]
|
||||||
|
@ -1355,7 +1355,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
Update(FIRST, Active),
|
Update(FIRST, Active), //
|
||||||
Render(FIRST, Active),
|
Render(FIRST, Active),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
@ -1366,7 +1366,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(FIRST, TransitionOut(TransitionTo::Paused), Active),
|
StateChange(FIRST, TransitionOut(TransitionTo::Paused), Active), //
|
||||||
Transition(FIRST, TransitionOut(TransitionTo::Paused)),
|
Transition(FIRST, TransitionOut(TransitionTo::Paused)),
|
||||||
Update(FIRST, TransitionOut(TransitionTo::Paused)),
|
Update(FIRST, TransitionOut(TransitionTo::Paused)),
|
||||||
Render(FIRST, TransitionOut(TransitionTo::Paused)),
|
Render(FIRST, TransitionOut(TransitionTo::Paused)),
|
||||||
|
@ -1377,7 +1377,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(FIRST, Paused, TransitionOut(TransitionTo::Paused)),
|
StateChange(FIRST, Paused, TransitionOut(TransitionTo::Paused)), //
|
||||||
StateChange(SECOND, Pending, Dead),
|
StateChange(SECOND, Pending, Dead),
|
||||||
StateChange(SECOND, TransitionIn, Pending),
|
StateChange(SECOND, TransitionIn, Pending),
|
||||||
Transition(SECOND, TransitionIn),
|
Transition(SECOND, TransitionIn),
|
||||||
|
@ -1390,7 +1390,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(SECOND, Active, TransitionIn),
|
StateChange(SECOND, Active, TransitionIn), //
|
||||||
Update(SECOND, Active),
|
Update(SECOND, Active),
|
||||||
Render(SECOND, Active),
|
Render(SECOND, Active),
|
||||||
]
|
]
|
||||||
|
@ -1401,7 +1401,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
Update(SECOND, Active),
|
Update(SECOND, Active), //
|
||||||
Render(SECOND, Active),
|
Render(SECOND, Active),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
@ -1412,7 +1412,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(SECOND, TransitionOut(TransitionTo::Dead), Active),
|
StateChange(SECOND, TransitionOut(TransitionTo::Dead), Active), //
|
||||||
Transition(SECOND, TransitionOut(TransitionTo::Dead)),
|
Transition(SECOND, TransitionOut(TransitionTo::Dead)),
|
||||||
Update(SECOND, TransitionOut(TransitionTo::Dead)),
|
Update(SECOND, TransitionOut(TransitionTo::Dead)),
|
||||||
Render(SECOND, TransitionOut(TransitionTo::Dead)),
|
Render(SECOND, TransitionOut(TransitionTo::Dead)),
|
||||||
|
@ -1424,7 +1424,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(SECOND, Dead, TransitionOut(TransitionTo::Dead)),
|
StateChange(SECOND, Dead, TransitionOut(TransitionTo::Dead)), //
|
||||||
StateChange(FIRST, Resume, Paused),
|
StateChange(FIRST, Resume, Paused),
|
||||||
StateChange(FIRST, TransitionIn, Resume),
|
StateChange(FIRST, TransitionIn, Resume),
|
||||||
Transition(FIRST, TransitionIn),
|
Transition(FIRST, TransitionIn),
|
||||||
|
@ -1437,7 +1437,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(FIRST, Active, TransitionIn),
|
StateChange(FIRST, Active, TransitionIn), //
|
||||||
Update(FIRST, Active),
|
Update(FIRST, Active),
|
||||||
Render(FIRST, Active),
|
Render(FIRST, Active),
|
||||||
]
|
]
|
||||||
|
@ -1448,7 +1448,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
Update(FIRST, Active),
|
Update(FIRST, Active), //
|
||||||
Render(FIRST, Active),
|
Render(FIRST, Active),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
@ -1459,7 +1459,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(FIRST, TransitionOut(TransitionTo::Dead), Active),
|
StateChange(FIRST, TransitionOut(TransitionTo::Dead), Active), //
|
||||||
Transition(FIRST, TransitionOut(TransitionTo::Dead)),
|
Transition(FIRST, TransitionOut(TransitionTo::Dead)),
|
||||||
Update(FIRST, TransitionOut(TransitionTo::Dead)),
|
Update(FIRST, TransitionOut(TransitionTo::Dead)),
|
||||||
Render(FIRST, TransitionOut(TransitionTo::Dead)),
|
Render(FIRST, TransitionOut(TransitionTo::Dead)),
|
||||||
|
@ -1494,7 +1494,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(FOO, Pending, Dead),
|
StateChange(FOO, Pending, Dead), //
|
||||||
StateChange(FOO, TransitionIn, Pending),
|
StateChange(FOO, TransitionIn, Pending),
|
||||||
Transition(FOO, TransitionIn),
|
Transition(FOO, TransitionIn),
|
||||||
Update(FOO, TransitionIn),
|
Update(FOO, TransitionIn),
|
||||||
|
@ -1510,7 +1510,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(FOO, Active, TransitionIn),
|
StateChange(FOO, Active, TransitionIn), //
|
||||||
Update(FOO, Active),
|
Update(FOO, Active),
|
||||||
Render(FOO, Active),
|
Render(FOO, Active),
|
||||||
]
|
]
|
||||||
|
@ -1524,7 +1524,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.take_log(),
|
context.take_log(),
|
||||||
vec![
|
vec![
|
||||||
StateChange(FOO, TransitionOut(TransitionTo::Dead), Active),
|
StateChange(FOO, TransitionOut(TransitionTo::Dead), Active), //
|
||||||
Transition(FOO, TransitionOut(TransitionTo::Dead)),
|
Transition(FOO, TransitionOut(TransitionTo::Dead)),
|
||||||
Update(FOO, TransitionOut(TransitionTo::Dead)),
|
Update(FOO, TransitionOut(TransitionTo::Dead)),
|
||||||
Render(FOO, TransitionOut(TransitionTo::Dead)),
|
Render(FOO, TransitionOut(TransitionTo::Dead)),
|
||||||
|
|
|
@ -58,33 +58,33 @@ impl From<sdl2::event::WindowEvent> for WindowEvent {
|
||||||
}
|
}
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
pub struct KeyModifiers: u16 {
|
pub struct KeyModifiers: u16 {
|
||||||
const NOMOD = 0x0000;
|
const NOMOD = 0x0000;
|
||||||
const LSHIFTMOD = 0x0001;
|
const LSHIFTMOD = 0x0001;
|
||||||
const RSHIFTMOD = 0x0002;
|
const RSHIFTMOD = 0x0002;
|
||||||
const LCTRLMOD = 0x0040;
|
const LCTRLMOD = 0x0040;
|
||||||
const RCTRLMOD = 0x0080;
|
const RCTRLMOD = 0x0080;
|
||||||
const LALTMOD = 0x0100;
|
const LALTMOD = 0x0100;
|
||||||
const RALTMOD = 0x0200;
|
const RALTMOD = 0x0200;
|
||||||
const LGUIMOD = 0x0400;
|
const LGUIMOD = 0x0400;
|
||||||
const RGUIMOD = 0x0800;
|
const RGUIMOD = 0x0800;
|
||||||
const NUMMOD = 0x1000;
|
const NUMMOD = 0x1000;
|
||||||
const CAPSMOD = 0x2000;
|
const CAPSMOD = 0x2000;
|
||||||
const MODEMOD = 0x4000;
|
const MODEMOD = 0x4000;
|
||||||
const RESERVEDMOD = 0x8000;
|
const RESERVEDMOD = 0x8000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum KeyboardEvent {
|
pub enum KeyboardEvent {
|
||||||
KeyUp {
|
KeyUp {
|
||||||
keycode: Option<Keycode>,
|
keycode: Option<Keycode>, //
|
||||||
scancode: Option<Scancode>,
|
scancode: Option<Scancode>,
|
||||||
keymod: KeyModifiers,
|
keymod: KeyModifiers,
|
||||||
repeat: bool,
|
repeat: bool,
|
||||||
},
|
},
|
||||||
KeyDown {
|
KeyDown {
|
||||||
keycode: Option<Keycode>,
|
keycode: Option<Keycode>, //
|
||||||
scancode: Option<Scancode>,
|
scancode: Option<Scancode>,
|
||||||
keymod: KeyModifiers,
|
keymod: KeyModifiers,
|
||||||
repeat: bool,
|
repeat: bool,
|
||||||
|
@ -94,20 +94,20 @@ pub enum KeyboardEvent {
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum MouseEvent {
|
pub enum MouseEvent {
|
||||||
MouseMotion {
|
MouseMotion {
|
||||||
x: i32,
|
x: i32, //
|
||||||
y: i32,
|
y: i32,
|
||||||
x_delta: i32,
|
x_delta: i32,
|
||||||
y_delta: i32,
|
y_delta: i32,
|
||||||
buttons: MouseButtons,
|
buttons: MouseButtons,
|
||||||
},
|
},
|
||||||
MouseButtonDown {
|
MouseButtonDown {
|
||||||
x: i32,
|
x: i32, //
|
||||||
y: i32,
|
y: i32,
|
||||||
button: MouseButton,
|
button: MouseButton,
|
||||||
clicks: u8,
|
clicks: u8,
|
||||||
},
|
},
|
||||||
MouseButtonUp {
|
MouseButtonUp {
|
||||||
x: i32,
|
x: i32, //
|
||||||
y: i32,
|
y: i32,
|
||||||
button: MouseButton,
|
button: MouseButton,
|
||||||
clicks: u8,
|
clicks: u8,
|
||||||
|
@ -168,20 +168,10 @@ impl From<sdl2::event::Event> for SystemEvent {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
sdl2::event::Event::MouseButtonDown { mouse_btn, clicks, x, y, .. } => {
|
sdl2::event::Event::MouseButtonDown { mouse_btn, clicks, x, y, .. } => {
|
||||||
SystemEvent::Mouse(MouseEvent::MouseButtonDown {
|
SystemEvent::Mouse(MouseEvent::MouseButtonDown { x, y, clicks, button: mouse_btn.into() })
|
||||||
x,
|
|
||||||
y,
|
|
||||||
clicks,
|
|
||||||
button: mouse_btn.into(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
sdl2::event::Event::MouseButtonUp { mouse_btn, clicks, x, y, .. } => {
|
sdl2::event::Event::MouseButtonUp { mouse_btn, clicks, x, y, .. } => {
|
||||||
SystemEvent::Mouse(MouseEvent::MouseButtonUp {
|
SystemEvent::Mouse(MouseEvent::MouseButtonUp { x, y, clicks, button: mouse_btn.into() })
|
||||||
x,
|
|
||||||
y,
|
|
||||||
clicks,
|
|
||||||
button: mouse_btn.into(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => SystemEvent::Unimplemented,
|
_ => SystemEvent::Unimplemented,
|
||||||
|
@ -218,9 +208,7 @@ pub struct SystemEventPump {
|
||||||
|
|
||||||
impl SystemEventPump {
|
impl SystemEventPump {
|
||||||
pub fn from(pump: sdl2::EventPump) -> Self {
|
pub fn from(pump: sdl2::EventPump) -> Self {
|
||||||
SystemEventPump {
|
SystemEventPump { sdl_event_pump: pump }
|
||||||
sdl_event_pump: pump,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an iterator over [`SystemEvent`]s that have been generated since the last time
|
/// Returns an iterator over [`SystemEvent`]s that have been generated since the last time
|
||||||
|
|
|
@ -480,4 +480,4 @@ impl From<sdl2::keyboard::Keycode> for Keycode {
|
||||||
sdl2::keyboard::Keycode::Sleep => Keycode::Sleep,
|
sdl2::keyboard::Keycode::Sleep => Keycode::Sleep,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::system::event::{KeyboardEvent, SystemEvent, SystemEventHandler};
|
use crate::system::event::{KeyboardEvent, SystemEvent, SystemEventHandler};
|
||||||
use crate::system::input_devices::{ButtonState, InputDevice};
|
|
||||||
use crate::system::input_devices::keyboard::scancodes::Scancode;
|
use crate::system::input_devices::keyboard::scancodes::Scancode;
|
||||||
|
use crate::system::input_devices::{ButtonState, InputDevice};
|
||||||
|
|
||||||
pub mod codes;
|
pub mod codes;
|
||||||
pub mod scancodes;
|
pub mod scancodes;
|
||||||
|
@ -21,9 +21,7 @@ pub struct Keyboard {
|
||||||
|
|
||||||
impl Keyboard {
|
impl Keyboard {
|
||||||
pub fn new() -> Keyboard {
|
pub fn new() -> Keyboard {
|
||||||
Keyboard {
|
Keyboard { keyboard: [ButtonState::Idle; MAX_KEYS] }
|
||||||
keyboard: [ButtonState::Idle; MAX_KEYS],
|
|
||||||
}
|
|
||||||
/*
|
/*
|
||||||
Keyboard {
|
Keyboard {
|
||||||
keyboard: vec![ButtonState::Idle; 256].into_boxed_slice(),
|
keyboard: vec![ButtonState::Idle; 256].into_boxed_slice(),
|
||||||
|
@ -34,19 +32,13 @@ impl Keyboard {
|
||||||
/// Returns true if the given key was just pressed or is being held down.
|
/// Returns true if the given key was just pressed or is being held down.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_key_down(&self, scancode: Scancode) -> bool {
|
pub fn is_key_down(&self, scancode: Scancode) -> bool {
|
||||||
matches!(
|
matches!(self.keyboard[scancode as usize], ButtonState::Pressed | ButtonState::Held)
|
||||||
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.
|
/// Returns true if the given key was not just pressed and is not being held down.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_key_up(&self, scancode: Scancode) -> bool {
|
pub fn is_key_up(&self, scancode: Scancode) -> bool {
|
||||||
matches!(
|
matches!(self.keyboard[scancode as usize], ButtonState::Released | ButtonState::Idle)
|
||||||
self.keyboard[scancode as usize],
|
|
||||||
ButtonState::Released | ButtonState::Idle
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if the given key was just pressed (not being held down, yet).
|
/// Returns true if the given key was just pressed (not being held down, yet).
|
||||||
|
@ -97,4 +89,4 @@ impl SystemEventHandler for Keyboard {
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -492,4 +492,4 @@ impl From<sdl2::keyboard::Scancode> for Scancode {
|
||||||
sdl2::keyboard::Scancode::Num => Scancode::Num,
|
sdl2::keyboard::Scancode::Num => Scancode::Num,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
pub struct MouseButtons: u32 {
|
pub struct MouseButtons: u32 {
|
||||||
const LEFT_BUTTON = sdl2::mouse::MouseButton::Left as u32;
|
const LEFT_BUTTON = sdl2::mouse::MouseButton::Left as u32;
|
||||||
const MIDDLE_BUTTON = sdl2::mouse::MouseButton::Middle as u32;
|
const MIDDLE_BUTTON = sdl2::mouse::MouseButton::Middle as u32;
|
||||||
const RIGHT_BUTTON = sdl2::mouse::MouseButton::Right as u32;
|
const RIGHT_BUTTON = sdl2::mouse::MouseButton::Right as u32;
|
||||||
const X1 = sdl2::mouse::MouseButton::X1 as u32;
|
const X1 = sdl2::mouse::MouseButton::X1 as u32;
|
||||||
const X2 = sdl2::mouse::MouseButton::X2 as u32;
|
const X2 = sdl2::mouse::MouseButton::X2 as u32;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
|
@ -29,7 +29,7 @@ impl From<sdl2::mouse::MouseButton> for MouseButton {
|
||||||
sdl2::mouse::MouseButton::Middle => MouseButton::Middle,
|
sdl2::mouse::MouseButton::Middle => MouseButton::Middle,
|
||||||
sdl2::mouse::MouseButton::Right => MouseButton::Right,
|
sdl2::mouse::MouseButton::Right => MouseButton::Right,
|
||||||
sdl2::mouse::MouseButton::X1 => MouseButton::X1,
|
sdl2::mouse::MouseButton::X1 => MouseButton::X1,
|
||||||
sdl2::mouse::MouseButton::X2 => MouseButton::X2
|
sdl2::mouse::MouseButton::X2 => MouseButton::X2,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ const DEFAULT_MOUSE_CURSOR_HEIGHT: usize = 16;
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct MouseCursorBitmap<BitmapType>
|
pub struct MouseCursorBitmap<BitmapType>
|
||||||
where
|
where
|
||||||
BitmapType: GeneralBitmap
|
BitmapType: GeneralBitmap,
|
||||||
{
|
{
|
||||||
pub cursor: BitmapType,
|
pub cursor: BitmapType,
|
||||||
pub hotspot_x: u32,
|
pub hotspot_x: u32,
|
||||||
|
@ -22,7 +22,7 @@ where
|
||||||
|
|
||||||
pub trait DefaultMouseCursorBitmaps<BitmapType>
|
pub trait DefaultMouseCursorBitmaps<BitmapType>
|
||||||
where
|
where
|
||||||
BitmapType: GeneralBitmap
|
BitmapType: GeneralBitmap,
|
||||||
{
|
{
|
||||||
fn get_default() -> MouseCursorBitmap<BitmapType>;
|
fn get_default() -> MouseCursorBitmap<BitmapType>;
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ where
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct CustomMouseCursor<BitmapType>
|
pub struct CustomMouseCursor<BitmapType>
|
||||||
where
|
where
|
||||||
BitmapType: GeneralBitmap
|
BitmapType: GeneralBitmap,
|
||||||
{
|
{
|
||||||
last_x: i32,
|
last_x: i32,
|
||||||
last_y: i32,
|
last_y: i32,
|
||||||
|
@ -48,14 +48,11 @@ where
|
||||||
impl<BitmapType> CustomMouseCursor<BitmapType>
|
impl<BitmapType> CustomMouseCursor<BitmapType>
|
||||||
where
|
where
|
||||||
Self: DefaultMouseCursorBitmaps<BitmapType>,
|
Self: DefaultMouseCursorBitmaps<BitmapType>,
|
||||||
BitmapType: GeneralBitmap
|
BitmapType: GeneralBitmap,
|
||||||
{
|
{
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let default_cursor = Self::get_default();
|
let default_cursor = Self::get_default();
|
||||||
let background = BitmapType::new(
|
let background = BitmapType::new(default_cursor.cursor.width(), default_cursor.cursor.height()).unwrap();
|
||||||
default_cursor.cursor.width(),
|
|
||||||
default_cursor.cursor.height()
|
|
||||||
).unwrap();
|
|
||||||
|
|
||||||
CustomMouseCursor {
|
CustomMouseCursor {
|
||||||
last_x: 0,
|
last_x: 0,
|
||||||
|
@ -108,7 +105,13 @@ where
|
||||||
/// * `cursor`: the bitmap to be used to display the mouse cursor on screen
|
/// * `cursor`: the bitmap to be used to display the mouse cursor on screen
|
||||||
/// * `hotspot_x`: the "hotspot" x coordinate
|
/// * `hotspot_x`: the "hotspot" x coordinate
|
||||||
/// * `hotspot_y`: the "hotspot" y coordinate.
|
/// * `hotspot_y`: the "hotspot" y coordinate.
|
||||||
pub fn set_mouse_cursor(&mut self, cursor: BitmapType, transparent_color: BitmapType::PixelType, hotspot_x: u32, hotspot_y: u32) {
|
pub fn set_mouse_cursor(
|
||||||
|
&mut self,
|
||||||
|
cursor: BitmapType,
|
||||||
|
transparent_color: BitmapType::PixelType,
|
||||||
|
hotspot_x: u32,
|
||||||
|
hotspot_y: u32,
|
||||||
|
) {
|
||||||
self.cursor = cursor;
|
self.cursor = cursor;
|
||||||
self.cursor_hotspot_x = hotspot_x;
|
self.cursor_hotspot_x = hotspot_x;
|
||||||
self.cursor_hotspot_y = hotspot_y;
|
self.cursor_hotspot_y = hotspot_y;
|
||||||
|
@ -134,7 +137,7 @@ where
|
||||||
#[inline]
|
#[inline]
|
||||||
fn get_cursor_render_position(&self) -> (i32, i32) {
|
fn get_cursor_render_position(&self) -> (i32, i32) {
|
||||||
(
|
(
|
||||||
self.last_x - self.cursor_hotspot_x as i32,
|
self.last_x - self.cursor_hotspot_x as i32, //
|
||||||
self.last_y - self.cursor_hotspot_y as i32,
|
self.last_y - self.cursor_hotspot_y as i32,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -167,12 +170,7 @@ where
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
|
|
||||||
dest.blit(
|
dest.blit(GeneralBlitMethod::Transparent(self.cursor_transparent_color), &self.cursor, x, y);
|
||||||
GeneralBlitMethod::Transparent(self.cursor_transparent_color),
|
|
||||||
&self.cursor,
|
|
||||||
x,
|
|
||||||
y
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Restores the original destination bitmap contents where the mouse cursor bitmap was
|
/// Restores the original destination bitmap contents where the mouse cursor bitmap was
|
||||||
|
@ -228,10 +226,8 @@ impl DefaultMouseCursorBitmaps<IndexedBitmap> for CustomMouseCursor<IndexedBitma
|
||||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut cursor = IndexedBitmap::new(
|
let mut cursor =
|
||||||
DEFAULT_MOUSE_CURSOR_WIDTH as u32,
|
IndexedBitmap::new(DEFAULT_MOUSE_CURSOR_WIDTH as u32, DEFAULT_MOUSE_CURSOR_HEIGHT as u32).unwrap();
|
||||||
DEFAULT_MOUSE_CURSOR_HEIGHT as u32,
|
|
||||||
).unwrap();
|
|
||||||
cursor.pixels_mut().copy_from_slice(&CURSOR_PIXELS);
|
cursor.pixels_mut().copy_from_slice(&CURSOR_PIXELS);
|
||||||
|
|
||||||
MouseCursorBitmap {
|
MouseCursorBitmap {
|
||||||
|
@ -265,10 +261,8 @@ impl DefaultMouseCursorBitmaps<RgbaBitmap> for CustomMouseCursor<RgbaBitmap> {
|
||||||
0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0x00000000, 0x00000000, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff
|
0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0x00000000, 0x00000000, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut cursor = RgbaBitmap::new(
|
let mut cursor =
|
||||||
DEFAULT_MOUSE_CURSOR_WIDTH as u32,
|
RgbaBitmap::new(DEFAULT_MOUSE_CURSOR_WIDTH as u32, DEFAULT_MOUSE_CURSOR_HEIGHT as u32).unwrap();
|
||||||
DEFAULT_MOUSE_CURSOR_HEIGHT as u32,
|
|
||||||
).unwrap();
|
|
||||||
cursor.pixels_mut().copy_from_slice(&CURSOR_PIXELS);
|
cursor.pixels_mut().copy_from_slice(&CURSOR_PIXELS);
|
||||||
|
|
||||||
MouseCursorBitmap {
|
MouseCursorBitmap {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::system::event::{MouseEvent, SystemEvent, SystemEventHandler};
|
use crate::system::event::{MouseEvent, SystemEvent, SystemEventHandler};
|
||||||
use crate::system::input_devices::{ButtonState, InputDevice};
|
|
||||||
use crate::system::input_devices::mouse::buttons::{MouseButton, MouseButtons};
|
use crate::system::input_devices::mouse::buttons::{MouseButton, MouseButtons};
|
||||||
|
use crate::system::input_devices::{ButtonState, InputDevice};
|
||||||
|
|
||||||
pub mod buttons;
|
pub mod buttons;
|
||||||
pub mod cursor;
|
pub mod cursor;
|
||||||
|
@ -26,7 +26,7 @@ pub struct Mouse {
|
||||||
impl Mouse {
|
impl Mouse {
|
||||||
pub fn new() -> Mouse {
|
pub fn new() -> Mouse {
|
||||||
Mouse {
|
Mouse {
|
||||||
x: 0,
|
x: 0, //
|
||||||
y: 0,
|
y: 0,
|
||||||
x_delta: 0,
|
x_delta: 0,
|
||||||
y_delta: 0,
|
y_delta: 0,
|
||||||
|
@ -63,19 +63,13 @@ impl Mouse {
|
||||||
/// Returns true if the given button was just pressed or is being held down.
|
/// Returns true if the given button was just pressed or is being held down.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_button_down(&self, button: usize) -> bool {
|
pub fn is_button_down(&self, button: usize) -> bool {
|
||||||
matches!(
|
matches!(self.buttons[button], ButtonState::Pressed | ButtonState::Held)
|
||||||
self.buttons[button],
|
|
||||||
ButtonState::Pressed | ButtonState::Held
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if the given button was not just pressed and is not being held down.
|
/// Returns true if the given button was not just pressed and is not being held down.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_button_up(&self, button: usize) -> bool {
|
pub fn is_button_up(&self, button: usize) -> bool {
|
||||||
matches!(
|
matches!(self.buttons[button], ButtonState::Released | ButtonState::Idle)
|
||||||
self.buttons[button],
|
|
||||||
ButtonState::Released | ButtonState::Idle
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if the given button was just pressed (not being held down, yet).
|
/// Returns true if the given button was just pressed (not being held down, yet).
|
||||||
|
@ -125,13 +119,7 @@ impl InputDevice for Mouse {
|
||||||
impl SystemEventHandler for Mouse {
|
impl SystemEventHandler for Mouse {
|
||||||
fn handle_event(&mut self, event: &SystemEvent) -> bool {
|
fn handle_event(&mut self, event: &SystemEvent) -> bool {
|
||||||
match event {
|
match event {
|
||||||
SystemEvent::Mouse(MouseEvent::MouseMotion {
|
SystemEvent::Mouse(MouseEvent::MouseMotion { x, y, x_delta, y_delta, buttons }) => {
|
||||||
x,
|
|
||||||
y,
|
|
||||||
x_delta,
|
|
||||||
y_delta,
|
|
||||||
buttons,
|
|
||||||
}) => {
|
|
||||||
self.x = *x;
|
self.x = *x;
|
||||||
self.y = *y;
|
self.y = *y;
|
||||||
self.x_delta = *x_delta;
|
self.x_delta = *x_delta;
|
||||||
|
@ -155,4 +143,4 @@ impl SystemEventHandler for Mouse {
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,12 +32,10 @@ fn is_x11_compositor_skipping_problematic() -> bool {
|
||||||
to check for this.
|
to check for this.
|
||||||
*/
|
*/
|
||||||
match std::env::consts::OS {
|
match std::env::consts::OS {
|
||||||
"linux" | "freebsd" | "netbsd" | "openbsd" => {
|
"linux" | "freebsd" | "netbsd" | "openbsd" => match std::env::var("XDG_SESSION_DESKTOP") {
|
||||||
match std::env::var("XDG_SESSION_DESKTOP") {
|
Ok(value) => value.eq_ignore_ascii_case("KDE"),
|
||||||
Ok(value) => value.eq_ignore_ascii_case("KDE"),
|
Err(_) => false,
|
||||||
Err(_) => false
|
},
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -140,7 +138,6 @@ impl SystemBuilder {
|
||||||
&self,
|
&self,
|
||||||
config: ConfigType,
|
config: ConfigType,
|
||||||
) -> Result<System<ConfigType::SystemResourcesType>, SystemError> {
|
) -> Result<System<ConfigType::SystemResourcesType>, SystemError> {
|
||||||
|
|
||||||
sdl2::hint::set("SDL_RENDER_VSYNC", if self.vsync { "1" } else { "0" });
|
sdl2::hint::set("SDL_RENDER_VSYNC", if self.vsync { "1" } else { "0" });
|
||||||
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" });
|
||||||
|
@ -176,7 +173,7 @@ impl SystemBuilder {
|
||||||
// SystemResources initialization
|
// SystemResources initialization
|
||||||
|
|
||||||
let mut window_builder = &mut (sdl_video_subsystem.window(
|
let mut window_builder = &mut (sdl_video_subsystem.window(
|
||||||
self.window_title.as_str(),
|
self.window_title.as_str(), //
|
||||||
640,
|
640,
|
||||||
480,
|
480,
|
||||||
));
|
));
|
||||||
|
@ -190,11 +187,7 @@ impl SystemBuilder {
|
||||||
|
|
||||||
sdl_context.mouse().show_cursor(self.show_mouse);
|
sdl_context.mouse().show_cursor(self.show_mouse);
|
||||||
|
|
||||||
let system_resources = match config.build(
|
let system_resources = match config.build(&sdl_video_subsystem, &sdl_audio_subsystem, sdl_window) {
|
||||||
&sdl_video_subsystem,
|
|
||||||
&sdl_audio_subsystem,
|
|
||||||
sdl_window
|
|
||||||
) {
|
|
||||||
Ok(system_resources) => system_resources,
|
Ok(system_resources) => system_resources,
|
||||||
Err(error) => return Err(SystemError::SystemResourcesError(error)),
|
Err(error) => return Err(SystemError::SystemResourcesError(error)),
|
||||||
};
|
};
|
||||||
|
@ -221,7 +214,9 @@ impl SystemBuilder {
|
||||||
/// "virtual machine" exposed by this library.
|
/// "virtual machine" exposed by this library.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub struct System<SystemResType>
|
pub struct System<SystemResType>
|
||||||
where SystemResType: SystemResources {
|
where
|
||||||
|
SystemResType: SystemResources,
|
||||||
|
{
|
||||||
sdl_context: sdl2::Sdl,
|
sdl_context: sdl2::Sdl,
|
||||||
sdl_audio_subsystem: sdl2::AudioSubsystem,
|
sdl_audio_subsystem: sdl2::AudioSubsystem,
|
||||||
sdl_video_subsystem: sdl2::VideoSubsystem,
|
sdl_video_subsystem: sdl2::VideoSubsystem,
|
||||||
|
@ -238,9 +233,11 @@ where SystemResType: SystemResources {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<SystemResType> std::fmt::Debug for System<SystemResType>
|
impl<SystemResType> std::fmt::Debug for System<SystemResType>
|
||||||
where SystemResType: SystemResources {
|
where
|
||||||
|
SystemResType: SystemResources,
|
||||||
|
{
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
f.debug_struct("System")
|
f.debug_struct("System") //
|
||||||
.field("res", &self.res)
|
.field("res", &self.res)
|
||||||
.field("vsync", &self.vsync)
|
.field("vsync", &self.vsync)
|
||||||
.field("target_framerate", &self.target_framerate)
|
.field("target_framerate", &self.target_framerate)
|
||||||
|
@ -251,7 +248,9 @@ where SystemResType: SystemResources {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<SystemResType> System<SystemResType>
|
impl<SystemResType> System<SystemResType>
|
||||||
where SystemResType: SystemResources {
|
where
|
||||||
|
SystemResType: SystemResources,
|
||||||
|
{
|
||||||
/// Displays the current backbuffer on to the window. If a `target_framerate` is set, this will
|
/// Displays the current backbuffer on to the window. If a `target_framerate` is set, this will
|
||||||
/// attempt to apply some timing to achieve that framerate. If V-sync is enabled, that will take
|
/// attempt to apply some timing to achieve that framerate. If V-sync is enabled, that will take
|
||||||
/// priority instead. You must call this in your application's main loop to display anything on screen.
|
/// priority instead. You must call this in your application's main loop to display anything on screen.
|
||||||
|
|
|
@ -1,22 +1,10 @@
|
||||||
pub use crate::system::{
|
pub use crate::system::{
|
||||||
*,
|
event::*, //
|
||||||
event::*,
|
|
||||||
input_devices::{
|
input_devices::{
|
||||||
|
keyboard::{codes::*, scancodes::*, *},
|
||||||
|
mouse::{buttons::*, cursor::*, *},
|
||||||
*,
|
*,
|
||||||
keyboard::{
|
|
||||||
*,
|
|
||||||
codes::*,
|
|
||||||
scancodes::*,
|
|
||||||
},
|
|
||||||
mouse::{
|
|
||||||
*,
|
|
||||||
buttons::*,
|
|
||||||
cursor::*,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
res::{
|
|
||||||
*,
|
|
||||||
// note: we are intentionally not including the `SystemResources` implementation modules here
|
|
||||||
},
|
},
|
||||||
|
res::*,
|
||||||
|
*,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -28,16 +28,16 @@
|
||||||
|
|
||||||
use byte_slice_cast::AsByteSlice;
|
use byte_slice_cast::AsByteSlice;
|
||||||
|
|
||||||
use crate::audio::{Audio, TARGET_AUDIO_CHANNELS, TARGET_AUDIO_FREQUENCY};
|
|
||||||
use crate::audio::queue::AudioQueue;
|
use crate::audio::queue::AudioQueue;
|
||||||
use crate::graphics::font::BitmaskFont;
|
use crate::audio::{Audio, TARGET_AUDIO_CHANNELS, TARGET_AUDIO_FREQUENCY};
|
||||||
use crate::graphics::bitmap::indexed::IndexedBitmap;
|
use crate::graphics::bitmap::indexed::IndexedBitmap;
|
||||||
|
use crate::graphics::font::BitmaskFont;
|
||||||
use crate::graphics::palette::Palette;
|
use crate::graphics::palette::Palette;
|
||||||
use crate::system::event::{SystemEvent, SystemEventHandler};
|
use crate::system::event::{SystemEvent, SystemEventHandler};
|
||||||
use crate::system::input_devices::InputDevice;
|
|
||||||
use crate::system::input_devices::keyboard::Keyboard;
|
use crate::system::input_devices::keyboard::Keyboard;
|
||||||
use crate::system::input_devices::mouse::cursor::CustomMouseCursor;
|
use crate::system::input_devices::mouse::cursor::CustomMouseCursor;
|
||||||
use crate::system::input_devices::mouse::Mouse;
|
use crate::system::input_devices::mouse::Mouse;
|
||||||
|
use crate::system::input_devices::InputDevice;
|
||||||
use crate::system::res::{SystemResources, SystemResourcesConfig, SystemResourcesError};
|
use crate::system::res::{SystemResources, SystemResourcesConfig, SystemResourcesError};
|
||||||
|
|
||||||
const DEFAULT_SCREEN_WIDTH: u32 = 320;
|
const DEFAULT_SCREEN_WIDTH: u32 = 320;
|
||||||
|
@ -113,7 +113,7 @@ impl SystemResourcesConfig for DosLikeConfig {
|
||||||
sdl2::sys::SDL_RenderSetIntegerScale(
|
sdl2::sys::SDL_RenderSetIntegerScale(
|
||||||
sdl_canvas.raw(),
|
sdl_canvas.raw(),
|
||||||
if self.integer_scaling {
|
if self.integer_scaling {
|
||||||
sdl2::sys::SDL_bool::SDL_TRUE
|
sdl2::sys::SDL_bool::SDL_TRUE //
|
||||||
} else {
|
} else {
|
||||||
sdl2::sys::SDL_bool::SDL_FALSE
|
sdl2::sys::SDL_bool::SDL_FALSE
|
||||||
},
|
},
|
||||||
|
@ -193,7 +193,7 @@ impl SystemResourcesConfig for DosLikeConfig {
|
||||||
keyboard,
|
keyboard,
|
||||||
mouse,
|
mouse,
|
||||||
cursor,
|
cursor,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,17 +240,17 @@ pub struct DosLike {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for DosLike {
|
impl std::fmt::Debug for DosLike {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
f.debug_struct("DosLike")
|
f.debug_struct("DosLike") //
|
||||||
.field("audio", &self.audio)
|
.field("audio", &self.audio)
|
||||||
.field("audio_queue", &self.audio_queue)
|
.field("audio_queue", &self.audio_queue)
|
||||||
.field("palette", &self.palette)
|
.field("palette", &self.palette)
|
||||||
.field("video", &self.video)
|
.field("video", &self.video)
|
||||||
.field("font", &self.font)
|
.field("font", &self.font)
|
||||||
.field("keyboard", &self.keyboard)
|
.field("keyboard", &self.keyboard)
|
||||||
.field("mouse", &self.mouse)
|
.field("mouse", &self.mouse)
|
||||||
.finish_non_exhaustive()
|
.finish_non_exhaustive()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SystemResources for DosLike {
|
impl SystemResources for DosLike {
|
||||||
|
@ -259,33 +259,33 @@ impl SystemResources for DosLike {
|
||||||
|
|
||||||
match self.audio_queue.apply(&mut self.audio) {
|
match self.audio_queue.apply(&mut self.audio) {
|
||||||
Ok(_) => Ok(()),
|
Ok(_) => Ok(()),
|
||||||
Err(error) => Err(SystemResourcesError::AudioDeviceError(error))
|
Err(error) => Err(SystemResourcesError::AudioDeviceError(error)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Takes the `video` backbuffer bitmap and `palette` and renders it to the window, up-scaled
|
/// Takes the `video` backbuffer bitmap and `palette` and renders it to the window, up-scaled
|
||||||
/// to fill the window (preserving aspect ratio of course).
|
/// to fill the window (preserving aspect ratio of course).
|
||||||
fn display(&mut self) -> Result<(), SystemResourcesError> {
|
fn display(&mut self) -> Result<(), SystemResourcesError> {
|
||||||
self.cursor.render(&mut self.video);
|
self.cursor.render(&mut self.video);
|
||||||
|
|
||||||
// convert application framebuffer to 32-bit RGBA pixels, and then upload it to the SDL
|
// convert application framebuffer to 32-bit RGBA pixels, and then upload it to the SDL
|
||||||
// texture so it will be displayed on screen
|
// 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();
|
let texture_pixels = self.texture_pixels.as_byte_slice();
|
||||||
if let Err(error) = self.sdl_texture.update(None, texture_pixels, self.sdl_texture_pitch) {
|
if let Err(error) = self.sdl_texture.update(None, texture_pixels, self.sdl_texture_pitch) {
|
||||||
return Err(SystemResourcesError::SDLError(error.to_string()));
|
return Err(SystemResourcesError::SDLError(error.to_string()));
|
||||||
}
|
}
|
||||||
self.sdl_canvas.clear();
|
self.sdl_canvas.clear();
|
||||||
if let Err(error) = self.sdl_canvas.copy(&self.sdl_texture, None, None) {
|
if let Err(error) = self.sdl_canvas.copy(&self.sdl_texture, None, None) {
|
||||||
return Err(SystemResourcesError::SDLError(error));
|
return Err(SystemResourcesError::SDLError(error));
|
||||||
}
|
}
|
||||||
self.sdl_canvas.present();
|
self.sdl_canvas.present();
|
||||||
|
|
||||||
self.cursor.hide(&mut self.video);
|
self.cursor.hide(&mut self.video);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_event_state(&mut self) -> Result<(), SystemResourcesError> {
|
fn update_event_state(&mut self) -> Result<(), SystemResourcesError> {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::audio::AudioError;
|
|
||||||
use crate::audio::device::AudioDeviceError;
|
use crate::audio::device::AudioDeviceError;
|
||||||
|
use crate::audio::AudioError;
|
||||||
use crate::system::event::SystemEvent;
|
use crate::system::event::SystemEvent;
|
||||||
|
|
||||||
pub mod dos_like;
|
pub mod dos_like;
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
use byte_slice_cast::AsByteSlice;
|
use byte_slice_cast::AsByteSlice;
|
||||||
|
|
||||||
use crate::audio::{Audio, TARGET_AUDIO_CHANNELS, TARGET_AUDIO_FREQUENCY};
|
|
||||||
use crate::audio::queue::AudioQueue;
|
use crate::audio::queue::AudioQueue;
|
||||||
|
use crate::audio::{Audio, TARGET_AUDIO_CHANNELS, TARGET_AUDIO_FREQUENCY};
|
||||||
use crate::graphics::bitmap::rgb::RgbaBitmap;
|
use crate::graphics::bitmap::rgb::RgbaBitmap;
|
||||||
use crate::graphics::font::BitmaskFont;
|
use crate::graphics::font::BitmaskFont;
|
||||||
use crate::system::event::{SystemEvent, SystemEventHandler};
|
use crate::system::event::{SystemEvent, SystemEventHandler};
|
||||||
use crate::system::input_devices::InputDevice;
|
|
||||||
use crate::system::input_devices::keyboard::Keyboard;
|
use crate::system::input_devices::keyboard::Keyboard;
|
||||||
use crate::system::input_devices::mouse::cursor::CustomMouseCursor;
|
use crate::system::input_devices::mouse::cursor::CustomMouseCursor;
|
||||||
use crate::system::input_devices::mouse::Mouse;
|
use crate::system::input_devices::mouse::Mouse;
|
||||||
|
use crate::system::input_devices::InputDevice;
|
||||||
use crate::system::res::{SystemResources, SystemResourcesConfig, SystemResourcesError};
|
use crate::system::res::{SystemResources, SystemResourcesConfig, SystemResourcesError};
|
||||||
|
|
||||||
const DEFAULT_SCREEN_WIDTH: u32 = 320;
|
const DEFAULT_SCREEN_WIDTH: u32 = 320;
|
||||||
|
@ -57,7 +57,7 @@ impl SystemResourcesConfig for StandardConfig {
|
||||||
self,
|
self,
|
||||||
_video_subsystem: &sdl2::VideoSubsystem,
|
_video_subsystem: &sdl2::VideoSubsystem,
|
||||||
audio_subsystem: &sdl2::AudioSubsystem,
|
audio_subsystem: &sdl2::AudioSubsystem,
|
||||||
mut window: sdl2::video::Window
|
mut window: sdl2::video::Window,
|
||||||
) -> Result<Self::SystemResourcesType, SystemResourcesError> {
|
) -> Result<Self::SystemResourcesType, SystemResourcesError> {
|
||||||
let texture_pixel_size = 4; // 32-bit ARGB format
|
let texture_pixel_size = 4; // 32-bit ARGB format
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ impl SystemResourcesConfig for StandardConfig {
|
||||||
sdl2::sys::SDL_RenderSetIntegerScale(
|
sdl2::sys::SDL_RenderSetIntegerScale(
|
||||||
sdl_canvas.raw(),
|
sdl_canvas.raw(),
|
||||||
if self.integer_scaling {
|
if self.integer_scaling {
|
||||||
sdl2::sys::SDL_bool::SDL_TRUE
|
sdl2::sys::SDL_bool::SDL_TRUE //
|
||||||
} else {
|
} else {
|
||||||
sdl2::sys::SDL_bool::SDL_FALSE
|
sdl2::sys::SDL_bool::SDL_FALSE
|
||||||
},
|
},
|
||||||
|
@ -147,7 +147,6 @@ impl SystemResourcesConfig for StandardConfig {
|
||||||
mouse,
|
mouse,
|
||||||
cursor,
|
cursor,
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,7 +187,7 @@ pub struct Standard {
|
||||||
|
|
||||||
impl std::fmt::Debug for Standard {
|
impl std::fmt::Debug for Standard {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
f.debug_struct("Standard")
|
f.debug_struct("Standard") //
|
||||||
.field("audio", &self.audio)
|
.field("audio", &self.audio)
|
||||||
.field("audio_queue", &self.audio_queue)
|
.field("audio_queue", &self.audio_queue)
|
||||||
.field("video", &self.video)
|
.field("video", &self.video)
|
||||||
|
@ -205,7 +204,7 @@ impl SystemResources for Standard {
|
||||||
|
|
||||||
match self.audio_queue.apply(&mut self.audio) {
|
match self.audio_queue.apply(&mut self.audio) {
|
||||||
Ok(_) => Ok(()),
|
Ok(_) => Ok(()),
|
||||||
Err(error) => Err(SystemResourcesError::AudioDeviceError(error))
|
Err(error) => Err(SystemResourcesError::AudioDeviceError(error)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,4 +19,4 @@ impl<T: std::io::Read + std::io::Seek> StreamSize for T {
|
||||||
|
|
||||||
Ok(len)
|
Ok(len)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -199,11 +199,7 @@ impl LzwBytesWriter {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_code<T: WriteBytesExt>(
|
pub fn write_code<T: WriteBytesExt>(&mut self, writer: &mut T, code: LzwCode) -> Result<(), LzwError> {
|
||||||
&mut self,
|
|
||||||
writer: &mut T,
|
|
||||||
code: LzwCode,
|
|
||||||
) -> Result<(), LzwError> {
|
|
||||||
self.packer.push_code(code)?;
|
self.packer.push_code(code)?;
|
||||||
|
|
||||||
while let Some(byte) = self.packer.take_byte() {
|
while let Some(byte) = self.packer.take_byte() {
|
||||||
|
@ -345,10 +341,7 @@ impl LzwBytesReader {
|
||||||
Ok(Some(reader.read_u8()?))
|
Ok(Some(reader.read_u8()?))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_code<T: ReadBytesExt>(
|
pub fn read_code<T: ReadBytesExt>(&mut self, reader: &mut T) -> Result<Option<LzwCode>, LzwError> {
|
||||||
&mut self,
|
|
||||||
reader: &mut T,
|
|
||||||
) -> Result<Option<LzwCode>, LzwError> {
|
|
||||||
loop {
|
loop {
|
||||||
if let Some(code) = self.unpacker.take_code() {
|
if let Some(code) = self.unpacker.take_code() {
|
||||||
return Ok(Some(code));
|
return Ok(Some(code));
|
||||||
|
@ -368,14 +361,10 @@ impl LzwBytesReader {
|
||||||
|
|
||||||
/// Encodes data read from the `src` using LZW (GIF-variant) compression, writing the encoded
|
/// 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`.
|
/// data out to `dest`. The LZW minimum code bit size is specified via `min_code_size`.
|
||||||
pub fn lzw_encode<S, D>(
|
pub fn lzw_encode<S, D>(src: &mut S, dest: &mut D, min_code_size: usize) -> Result<(), LzwError>
|
||||||
src: &mut S,
|
where
|
||||||
dest: &mut D,
|
S: ReadBytesExt,
|
||||||
min_code_size: usize,
|
D: WriteBytesExt,
|
||||||
) -> Result<(), LzwError>
|
|
||||||
where
|
|
||||||
S: ReadBytesExt,
|
|
||||||
D: WriteBytesExt
|
|
||||||
{
|
{
|
||||||
if !is_valid_gif_min_code_size_bits(min_code_size) {
|
if !is_valid_gif_min_code_size_bits(min_code_size) {
|
||||||
return Err(LzwError::UnsupportedCodeSizeBits(min_code_size));
|
return Err(LzwError::UnsupportedCodeSizeBits(min_code_size));
|
||||||
|
@ -414,7 +403,7 @@ pub fn lzw_encode<S, D>(
|
||||||
writer.flush(dest)?;
|
writer.flush(dest)?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
Err(error) => return Err(LzwError::IOError(error))
|
Err(error) => return Err(LzwError::IOError(error)),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut buffer = vec![byte];
|
let mut buffer = vec![byte];
|
||||||
|
@ -424,7 +413,7 @@ pub fn lzw_encode<S, D>(
|
||||||
let byte = match src.read_u8() {
|
let byte = match src.read_u8() {
|
||||||
Ok(byte) => byte,
|
Ok(byte) => byte,
|
||||||
Err(ref error) if error.kind() == std::io::ErrorKind::UnexpectedEof => break,
|
Err(ref error) if error.kind() == std::io::ErrorKind::UnexpectedEof => break,
|
||||||
Err(error) => return Err(LzwError::IOError(error))
|
Err(error) => return Err(LzwError::IOError(error)),
|
||||||
};
|
};
|
||||||
|
|
||||||
// check if the table currently contains a string composed of the current buffer plus
|
// check if the table currently contains a string composed of the current buffer plus
|
||||||
|
@ -447,7 +436,10 @@ pub fn lzw_encode<S, D>(
|
||||||
if let Some(code) = table.get(&buffer) {
|
if let Some(code) = table.get(&buffer) {
|
||||||
writer.write_code(dest, *code)?;
|
writer.write_code(dest, *code)?;
|
||||||
} else {
|
} else {
|
||||||
return Err(LzwError::EncodingError(format!("Expected to find code in table for buffer {:?} but none was found", buffer)));
|
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 ...
|
// bump up to the next bit size once we've seen enough codes to necessitate it ...
|
||||||
|
@ -485,7 +477,10 @@ pub fn lzw_encode<S, D>(
|
||||||
if let Some(code) = table.get(&buffer) {
|
if let Some(code) = table.get(&buffer) {
|
||||||
writer.write_code(dest, *code)?;
|
writer.write_code(dest, *code)?;
|
||||||
} else {
|
} else {
|
||||||
return Err(LzwError::EncodingError(format!("No matching code for buffer {:?} at end of input stream", buffer)));
|
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.write_code(dest, end_of_info_code)?;
|
||||||
|
@ -496,13 +491,10 @@ pub fn lzw_encode<S, D>(
|
||||||
|
|
||||||
/// Decodes data read from the `src` using LZW (GIF-variant) decompression, writing the decoded
|
/// Decodes data read from the `src` using LZW (GIF-variant) decompression, writing the decoded
|
||||||
/// data out to `dest`.
|
/// data out to `dest`.
|
||||||
pub fn lzw_decode<S, D>(
|
pub fn lzw_decode<S, D>(src: &mut S, dest: &mut D) -> Result<(), LzwError>
|
||||||
src: &mut S,
|
where
|
||||||
dest: &mut D,
|
S: ReadBytesExt,
|
||||||
) -> Result<(), LzwError>
|
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;
|
||||||
|
|
||||||
|
@ -525,7 +517,7 @@ pub fn lzw_decode<S, D>(
|
||||||
// are no codes to read (kind of a weird situation, but no real reason to error ...?)
|
// are no codes to read (kind of a weird situation, but no real reason to error ...?)
|
||||||
let mut code = match reader.read_code(src)? {
|
let mut code = match reader.read_code(src)? {
|
||||||
Some(code) => code,
|
Some(code) => code,
|
||||||
None => return Ok(())
|
None => return Ok(()),
|
||||||
};
|
};
|
||||||
|
|
||||||
// the first code in the stream SHOULD be a clear code ... which we can just ignore because
|
// the first code in the stream SHOULD be a clear code ... which we can just ignore because
|
||||||
|
@ -553,7 +545,9 @@ pub fn lzw_decode<S, D>(
|
||||||
|
|
||||||
// read the next code which should actually be the first "interesting" value of the code stream
|
// read the next code which should actually be the first "interesting" value of the code stream
|
||||||
code = match reader.read_code(src)? {
|
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 > 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) if code == end_of_info_code => return Ok(()),
|
||||||
Some(code) => code,
|
Some(code) => code,
|
||||||
None => return Err(LzwError::EncodingError(String::from("Unexpected end of code stream"))),
|
None => return Err(LzwError::EncodingError(String::from("Unexpected end of code stream"))),
|
||||||
|
@ -573,7 +567,9 @@ pub fn lzw_decode<S, D>(
|
||||||
'inner: loop {
|
'inner: loop {
|
||||||
// grab the next code
|
// grab the next code
|
||||||
code = match reader.read_code(src)? {
|
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 > 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 == end_of_info_code => break 'outer,
|
||||||
Some(code) if code == clear_code => {
|
Some(code) if code == clear_code => {
|
||||||
// reset the bit size and reader and then loop back to the outer loop which
|
// reset the bit size and reader and then loop back to the outer loop which
|
||||||
|
@ -645,19 +641,35 @@ mod tests {
|
||||||
static LZW_TEST_DATA: &[LzwTestData] = &[
|
static LZW_TEST_DATA: &[LzwTestData] = &[
|
||||||
LzwTestData {
|
LzwTestData {
|
||||||
min_code_size: 2,
|
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],
|
packed: &[
|
||||||
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],
|
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 {
|
LzwTestData {
|
||||||
min_code_size: 4,
|
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],
|
packed: &[
|
||||||
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],
|
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 {
|
LzwTestData {
|
||||||
min_code_size: 8,
|
min_code_size: 8,
|
||||||
packed: &[0x08, 0x0b, 0x00, 0x51, 0xfc, 0x1b, 0x28, 0x70, 0xa0, 0xc1, 0x83, 0x01, 0x01, 0x00],
|
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],
|
unpacked: &[0x28, 0xff, 0xff, 0xff, 0x28, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -39,4 +39,4 @@ impl<A: Any> AsAny for A {
|
||||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||||
self as &mut dyn Any
|
self as &mut dyn Any
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,9 +13,9 @@ enum PackMode {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pack_bits<S, D>(src: &mut S, dest: &mut D, src_length: usize) -> Result<(), PackBitsError>
|
pub fn pack_bits<S, D>(src: &mut S, dest: &mut D, src_length: usize) -> Result<(), PackBitsError>
|
||||||
where
|
where
|
||||||
S: ReadBytesExt,
|
S: ReadBytesExt,
|
||||||
D: WriteBytesExt,
|
D: WriteBytesExt,
|
||||||
{
|
{
|
||||||
const MIN_RUN: usize = 3;
|
const MIN_RUN: usize = 3;
|
||||||
const MAX_RUN: usize = 128;
|
const MAX_RUN: usize = 128;
|
||||||
|
@ -112,14 +112,10 @@ pub fn pack_bits<S, D>(src: &mut S, dest: &mut D, src_length: usize) -> Result<(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unpack_bits<S, D>(
|
pub fn unpack_bits<S, D>(src: &mut S, dest: &mut D, unpacked_length: usize) -> Result<(), PackBitsError>
|
||||||
src: &mut S,
|
where
|
||||||
dest: &mut D,
|
S: ReadBytesExt,
|
||||||
unpacked_length: usize,
|
D: WriteBytesExt,
|
||||||
) -> Result<(), PackBitsError>
|
|
||||||
where
|
|
||||||
S: ReadBytesExt,
|
|
||||||
D: WriteBytesExt,
|
|
||||||
{
|
{
|
||||||
let mut buffer = [0u8; 128];
|
let mut buffer = [0u8; 128];
|
||||||
let mut bytes_written = 0;
|
let mut bytes_written = 0;
|
||||||
|
@ -165,23 +161,14 @@ mod tests {
|
||||||
|
|
||||||
static TEST_DATA: &[TestData] = &[
|
static TEST_DATA: &[TestData] = &[
|
||||||
TestData {
|
TestData {
|
||||||
packed: &[
|
packed: &[0xfe, 0xaa, 0x02, 0x80, 0x00, 0x2a, 0xfd, 0xaa, 0x03, 0x80, 0x00, 0x2a, 0x22, 0xf7, 0xaa],
|
||||||
0xfe, 0xaa, 0x02, 0x80, 0x00, 0x2a, 0xfd, 0xaa, 0x03, 0x80, 0x00, 0x2a, 0x22, 0xf7,
|
|
||||||
0xaa,
|
|
||||||
],
|
|
||||||
unpacked: &[
|
unpacked: &[
|
||||||
0xaa, 0xaa, 0xaa, 0x80, 0x00, 0x2a, 0xaa, 0xaa, 0xaa, 0xaa, 0x80, 0x00, 0x2a, 0x22,
|
0xaa, 0xaa, 0xaa, 0x80, 0x00, 0x2a, 0xaa, 0xaa, 0xaa, 0xaa, 0x80, 0x00, 0x2a, 0x22, 0xaa, 0xaa, 0xaa,
|
||||||
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
|
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
TestData {
|
TestData { packed: &[0x00, 0xaa], unpacked: &[0xaa] },
|
||||||
packed: &[0x00, 0xaa],
|
TestData { packed: &[0xf9, 0xaa], unpacked: &[0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa] },
|
||||||
unpacked: &[0xaa],
|
|
||||||
},
|
|
||||||
TestData {
|
|
||||||
packed: &[0xf9, 0xaa],
|
|
||||||
unpacked: &[0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa],
|
|
||||||
},
|
|
||||||
TestData {
|
TestData {
|
||||||
packed: &[0xf9, 0xaa, 0x00, 0xbb],
|
packed: &[0xf9, 0xaa, 0x00, 0xbb],
|
||||||
unpacked: &[0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xbb],
|
unpacked: &[0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xbb],
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
pub use crate::utils::{
|
pub use crate::utils::{
|
||||||
*,
|
//
|
||||||
bytes::*,
|
bytes::*,
|
||||||
io::*,
|
io::*,
|
||||||
lzwgif::*,
|
lzwgif::*,
|
||||||
packbits::*,
|
packbits::*,
|
||||||
|
*,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -170,7 +170,6 @@ fn blended_pixel_drawing() {
|
||||||
assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path);
|
assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn horiz_line_drawing() {
|
fn horiz_line_drawing() {
|
||||||
let (mut screen, palette) = setup();
|
let (mut screen, palette) = setup();
|
||||||
|
@ -752,6 +751,7 @@ fn solid_flipped_blits() {
|
||||||
assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path);
|
assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
#[test]
|
#[test]
|
||||||
fn blended_solid_flipped_blits() {
|
fn blended_solid_flipped_blits() {
|
||||||
use IndexedBlitMethod::*;
|
use IndexedBlitMethod::*;
|
||||||
|
@ -884,6 +884,7 @@ fn solid_offset_blits() {
|
||||||
assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path);
|
assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
#[test]
|
#[test]
|
||||||
fn solid_flipped_offset_blits() {
|
fn solid_flipped_offset_blits() {
|
||||||
use IndexedBlitMethod::*;
|
use IndexedBlitMethod::*;
|
||||||
|
@ -1090,6 +1091,7 @@ fn blended_transparent_blits() {
|
||||||
assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path);
|
assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
#[test]
|
#[test]
|
||||||
fn transparent_flipped_blits() {
|
fn transparent_flipped_blits() {
|
||||||
use IndexedBlitMethod::*;
|
use IndexedBlitMethod::*;
|
||||||
|
@ -1156,6 +1158,7 @@ fn transparent_flipped_blits() {
|
||||||
assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path);
|
assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
#[test]
|
#[test]
|
||||||
fn blended_transparent_flipped_blits() {
|
fn blended_transparent_flipped_blits() {
|
||||||
use IndexedBlitMethod::*;
|
use IndexedBlitMethod::*;
|
||||||
|
@ -1292,6 +1295,7 @@ fn transparent_offset_blits() {
|
||||||
assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path);
|
assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
#[test]
|
#[test]
|
||||||
fn transparent_flipped_offset_blits() {
|
fn transparent_flipped_offset_blits() {
|
||||||
use IndexedBlitMethod::*;
|
use IndexedBlitMethod::*;
|
||||||
|
@ -1432,6 +1436,7 @@ fn transparent_single_blits() {
|
||||||
assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path);
|
assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
#[test]
|
#[test]
|
||||||
fn transparent_flipped_single_blits() {
|
fn transparent_flipped_single_blits() {
|
||||||
use IndexedBlitMethod::*;
|
use IndexedBlitMethod::*;
|
||||||
|
@ -1568,6 +1573,7 @@ fn rotozoom_blits() {
|
||||||
assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path);
|
assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
#[test]
|
#[test]
|
||||||
fn blended_rotozoom_blits() {
|
fn blended_rotozoom_blits() {
|
||||||
use IndexedBlitMethod::*;
|
use IndexedBlitMethod::*;
|
||||||
|
@ -1634,6 +1640,7 @@ fn blended_rotozoom_blits() {
|
||||||
assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path);
|
assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
#[test]
|
#[test]
|
||||||
fn rotozoom_offset_blits() {
|
fn rotozoom_offset_blits() {
|
||||||
use IndexedBlitMethod::*;
|
use IndexedBlitMethod::*;
|
||||||
|
@ -1702,6 +1709,7 @@ fn rotozoom_offset_blits() {
|
||||||
assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path);
|
assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
#[test]
|
#[test]
|
||||||
fn rotozoom_transparent_blits() {
|
fn rotozoom_transparent_blits() {
|
||||||
use IndexedBlitMethod::*;
|
use IndexedBlitMethod::*;
|
||||||
|
@ -1770,6 +1778,7 @@ fn rotozoom_transparent_blits() {
|
||||||
assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path);
|
assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
#[test]
|
#[test]
|
||||||
fn blended_rotozoom_transparent_blits() {
|
fn blended_rotozoom_transparent_blits() {
|
||||||
use IndexedBlitMethod::*;
|
use IndexedBlitMethod::*;
|
||||||
|
@ -1799,7 +1808,13 @@ fn blended_rotozoom_transparent_blits() {
|
||||||
|
|
||||||
//////
|
//////
|
||||||
|
|
||||||
let method = RotoZoomTransparentBlended { transparent_color, angle: 1.3, scale_x: 1.0, scale_y: 1.0, blend_map: blend_map.clone() };
|
let method = RotoZoomTransparentBlended {
|
||||||
|
transparent_color,
|
||||||
|
angle: 1.3,
|
||||||
|
scale_x: 1.0,
|
||||||
|
scale_y: 1.0,
|
||||||
|
blend_map: blend_map.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
screen.blit(method.clone(), &bmp, -3, 46);
|
screen.blit(method.clone(), &bmp, -3, 46);
|
||||||
screen.blit(method.clone(), &bmp, -4, 76);
|
screen.blit(method.clone(), &bmp, -4, 76);
|
||||||
|
@ -1838,6 +1853,7 @@ fn blended_rotozoom_transparent_blits() {
|
||||||
assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path);
|
assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
#[test]
|
#[test]
|
||||||
fn rotozoom_transparent_offset_blits() {
|
fn rotozoom_transparent_offset_blits() {
|
||||||
use IndexedBlitMethod::*;
|
use IndexedBlitMethod::*;
|
||||||
|
|
|
@ -602,7 +602,7 @@ fn generate_bitmap_with_varied_alpha(width: i32, height: i32) -> RgbaBitmap {
|
||||||
let y_third = height / 3;
|
let y_third = height / 3;
|
||||||
|
|
||||||
let mut bitmap = RgbaBitmap::new(width as u32, height as u32).unwrap();
|
let mut bitmap = RgbaBitmap::new(width as u32, height as u32).unwrap();
|
||||||
bitmap.clear(0); // alpha=0
|
bitmap.clear(0); // alpha=0
|
||||||
|
|
||||||
bitmap.filled_rect(0, 0, x_third, y_third, 0x330000aa);
|
bitmap.filled_rect(0, 0, x_third, y_third, 0x330000aa);
|
||||||
bitmap.filled_rect(x_third * 2 + 1, y_third * 2 + 1, width - 1, height - 1, 0x6600aa00);
|
bitmap.filled_rect(x_third * 2 + 1, y_third * 2 + 1, width - 1, height - 1, 0x6600aa00);
|
||||||
|
@ -901,6 +901,7 @@ fn solid_flipped_blits() {
|
||||||
assert!(verify_visual(&screen, &path), "bitmap differs from source image: {:?}", path);
|
assert!(verify_visual(&screen, &path), "bitmap differs from source image: {:?}", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
#[test]
|
#[test]
|
||||||
fn solid_flipped_tinted_blits() {
|
fn solid_flipped_tinted_blits() {
|
||||||
use RgbaBlitMethod::*;
|
use RgbaBlitMethod::*;
|
||||||
|
@ -967,6 +968,7 @@ fn solid_flipped_tinted_blits() {
|
||||||
assert!(verify_visual(&screen, &path), "bitmap differs from source image: {:?}", path);
|
assert!(verify_visual(&screen, &path), "bitmap differs from source image: {:?}", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
#[test]
|
#[test]
|
||||||
fn blended_solid_flipped_blits() {
|
fn blended_solid_flipped_blits() {
|
||||||
use RgbaBlitMethod::*;
|
use RgbaBlitMethod::*;
|
||||||
|
@ -1113,10 +1115,7 @@ fn transparent_tinted_blits() {
|
||||||
let bmp21 = generate_bitmap(21, 21);
|
let bmp21 = generate_bitmap(21, 21);
|
||||||
let bmp3 = generate_bitmap(3, 3);
|
let bmp3 = generate_bitmap(3, 3);
|
||||||
|
|
||||||
let method = TransparentTinted {
|
let method = TransparentTinted { transparent_color: to_rgb32(0, 0, 0), tint_color: to_argb32(127, 155, 242, 21) };
|
||||||
transparent_color: to_rgb32(0, 0, 0),
|
|
||||||
tint_color: to_argb32(127, 155, 242, 21)
|
|
||||||
};
|
|
||||||
|
|
||||||
let x = 40;
|
let x = 40;
|
||||||
let y = 20;
|
let y = 20;
|
||||||
|
@ -1241,6 +1240,7 @@ fn blended_transparent_blits() {
|
||||||
assert!(verify_visual(&screen, &path), "bitmap differs from source image: {:?}", path);
|
assert!(verify_visual(&screen, &path), "bitmap differs from source image: {:?}", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
#[test]
|
#[test]
|
||||||
fn transparent_flipped_blits() {
|
fn transparent_flipped_blits() {
|
||||||
use RgbaBlitMethod::*;
|
use RgbaBlitMethod::*;
|
||||||
|
@ -1307,6 +1307,7 @@ fn transparent_flipped_blits() {
|
||||||
assert!(verify_visual(&screen, &path), "bitmap differs from source image: {:?}", path);
|
assert!(verify_visual(&screen, &path), "bitmap differs from source image: {:?}", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
#[test]
|
#[test]
|
||||||
fn transparent_flipped_tinted_blits() {
|
fn transparent_flipped_tinted_blits() {
|
||||||
use RgbaBlitMethod::*;
|
use RgbaBlitMethod::*;
|
||||||
|
@ -1374,6 +1375,7 @@ fn transparent_flipped_tinted_blits() {
|
||||||
assert!(verify_visual(&screen, &path), "bitmap differs from source image: {:?}", path);
|
assert!(verify_visual(&screen, &path), "bitmap differs from source image: {:?}", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
#[test]
|
#[test]
|
||||||
fn blended_transparent_flipped_blits() {
|
fn blended_transparent_flipped_blits() {
|
||||||
use RgbaBlitMethod::*;
|
use RgbaBlitMethod::*;
|
||||||
|
@ -1440,6 +1442,7 @@ fn blended_transparent_flipped_blits() {
|
||||||
assert!(verify_visual(&screen, &path), "bitmap differs from source image: {:?}", path);
|
assert!(verify_visual(&screen, &path), "bitmap differs from source image: {:?}", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
#[test]
|
#[test]
|
||||||
fn transparent_single_blits() {
|
fn transparent_single_blits() {
|
||||||
use RgbaBlitMethod::*;
|
use RgbaBlitMethod::*;
|
||||||
|
@ -1510,6 +1513,7 @@ fn transparent_single_blits() {
|
||||||
assert!(verify_visual(&screen, &path), "bitmap differs from source image: {:?}", path);
|
assert!(verify_visual(&screen, &path), "bitmap differs from source image: {:?}", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
#[test]
|
#[test]
|
||||||
fn transparent_flipped_single_blits() {
|
fn transparent_flipped_single_blits() {
|
||||||
use RgbaBlitMethod::*;
|
use RgbaBlitMethod::*;
|
||||||
|
@ -1642,6 +1646,7 @@ fn rotozoom_blits() {
|
||||||
assert!(verify_visual(&screen, &path), "bitmap differs from source image: {:?}", path);
|
assert!(verify_visual(&screen, &path), "bitmap differs from source image: {:?}", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
#[test]
|
#[test]
|
||||||
fn rotozoom_tinted_blits() {
|
fn rotozoom_tinted_blits() {
|
||||||
use RgbaBlitMethod::*;
|
use RgbaBlitMethod::*;
|
||||||
|
@ -1777,6 +1782,7 @@ fn blended_rotozoom_blits() {
|
||||||
assert!(verify_visual(&screen, &path), "bitmap differs from source image: {:?}", path);
|
assert!(verify_visual(&screen, &path), "bitmap differs from source image: {:?}", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
#[test]
|
#[test]
|
||||||
fn rotozoom_transparent_blits() {
|
fn rotozoom_transparent_blits() {
|
||||||
use RgbaBlitMethod::*;
|
use RgbaBlitMethod::*;
|
||||||
|
@ -1845,6 +1851,7 @@ fn rotozoom_transparent_blits() {
|
||||||
assert!(verify_visual(&screen, &path), "bitmap differs from source image: {:?}", path);
|
assert!(verify_visual(&screen, &path), "bitmap differs from source image: {:?}", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
#[test]
|
#[test]
|
||||||
fn rotozoom_transparent_tinted_blits() {
|
fn rotozoom_transparent_tinted_blits() {
|
||||||
use RgbaBlitMethod::*;
|
use RgbaBlitMethod::*;
|
||||||
|
@ -1914,6 +1921,7 @@ fn rotozoom_transparent_tinted_blits() {
|
||||||
assert!(verify_visual(&screen, &path), "bitmap differs from source image: {:?}", path);
|
assert!(verify_visual(&screen, &path), "bitmap differs from source image: {:?}", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
#[test]
|
#[test]
|
||||||
fn blended_rotozoom_transparent_blits() {
|
fn blended_rotozoom_transparent_blits() {
|
||||||
use RgbaBlitMethod::*;
|
use RgbaBlitMethod::*;
|
||||||
|
|
Loading…
Reference in a new issue