Compare commits

..

No commits in common. "master" and "simd" have entirely different histories.
master ... simd

123 changed files with 1731 additions and 2657 deletions

View file

@ -4,14 +4,6 @@ members = [
"ggdt_imgui",
"examples/*",
]
resolver = "2"
[workspace.dependencies]
anyhow = "=1.0.75"
imgui = { version = "0.11.0", features = ["docking"] }
serde = { version = "1.0.192", features = ["derive"] }
serde_json = "1.0.108"
thiserror = "=1.0.50"
# some stuff is becoming noticably slow with default dev profile settings (opt-level = 0). especially any of the
# fancier triangle_2d drawing stuff. while there are many optimizations still left to be made, it seems like a good

View file

@ -4,5 +4,5 @@ version = "0.1.0"
edition = "2021"
[dependencies]
anyhow.workspace = true
anyhow = "=1.0.55"
ggdt = { path = "../../ggdt" }

View file

@ -11,15 +11,15 @@ struct AudioChannelStatus {
playing: bool,
}
fn load_and_convert_wav(path: impl AsRef<Path>, target_spec: &AudioSpec) -> Result<AudioBuffer> {
let sound = AudioBuffer::load_wav_file(&path)?;
fn load_and_convert_wav(path: &Path, target_spec: &AudioSpec) -> Result<AudioBuffer> {
let sound = AudioBuffer::load_wav_file(path)?;
let original_spec = *sound.spec();
let sound = sound.convert(target_spec)?;
let final_spec = *sound.spec();
if original_spec != final_spec {
println!("{:?} was converted from {:?} to {:?}", path.as_ref(), original_spec, final_spec);
println!("{:?} was converted from {:?} to {:?}", path, original_spec, final_spec);
} else {
println!("{:?} did not need to be converted from {:?}", path.as_ref(), original_spec);
println!("{:?} did not need to be converted from {:?}", path, original_spec);
}
Ok(sound)
}
@ -28,7 +28,6 @@ pub struct SineWaveGenerator {
t: usize,
}
#[allow(clippy::new_without_default)]
impl SineWaveGenerator {
pub fn new() -> Self {
SineWaveGenerator { t: 0 }
@ -59,11 +58,11 @@ fn main() -> Result<()> {
let mut volume = 1.0;
let sounds = [
load_and_convert_wav("./assets/pickup-coin.wav", system.res.audio.spec())?,
load_and_convert_wav("./assets/powerup.wav", system.res.audio.spec())?,
load_and_convert_wav("./assets/explosion.wav", system.res.audio.spec())?,
load_and_convert_wav("./assets/jump.wav", system.res.audio.spec())?,
load_and_convert_wav("./assets/laser-shoot.wav", system.res.audio.spec())?,
load_and_convert_wav(Path::new("./assets/pickup-coin.wav"), system.res.audio.spec())?,
load_and_convert_wav(Path::new("./assets/powerup.wav"), system.res.audio.spec())?,
load_and_convert_wav(Path::new("./assets/explosion.wav"), system.res.audio.spec())?,
load_and_convert_wav(Path::new("./assets/jump.wav"), system.res.audio.spec())?,
load_and_convert_wav(Path::new("./assets/laser-shoot.wav"), system.res.audio.spec())?,
];
let mut statuses = [AudioChannelStatus { size: 0, position: 0, playing: false }; NUM_CHANNELS];
@ -155,7 +154,7 @@ fn main() -> Result<()> {
for index in 0..NUM_CHANNELS {
let channel = &audio_device[index];
let status = &mut statuses[index];
let mut status = &mut statuses[index];
status.playing = channel.playing;
status.position = channel.position;
status.size = channel.data.len();
@ -184,7 +183,8 @@ fn main() -> Result<()> {
system.res.video.print_string("Audio Channels", 16, 32, FontRenderOpts::Color(14), &system.res.font);
let mut y = 48;
for (index, status) in statuses.iter().enumerate() {
for index in 0..NUM_CHANNELS {
let status = &statuses[index];
system.res.video.print_string(
&format!(
"channel {} - {} {}",

View file

@ -4,5 +4,5 @@ version = "0.1.0"
edition = "2021"
[dependencies]
anyhow.workspace = true
anyhow = "=1.0.55"
ggdt = { path = "../../ggdt" }

View file

@ -1,3 +1,5 @@
use std::path::Path;
use anyhow::Result;
use ggdt::prelude::*;
@ -24,7 +26,7 @@ fn main() -> Result<()> {
let font = BitmaskFont::new_vga_font()?;
let (balls_bmp, balls_palette) = IndexedBitmap::load_pcx_file("./assets/balls.pcx")?;
let (balls_bmp, balls_palette) = IndexedBitmap::load_pcx_file(Path::new("./assets/balls.pcx"))?;
system.res.palette = balls_palette.clone();
let mut sprites = Vec::<IndexedBitmap>::new();
@ -60,7 +62,8 @@ fn main() -> Result<()> {
}
if system.res.keyboard.is_key_up(Scancode::S) {
for ball in balls.iter_mut() {
for i in 0..NUM_BALLS {
let ball = &mut balls[i];
ball.x += ball.dir_x;
ball.y += ball.dir_y;
@ -69,22 +72,26 @@ fn main() -> Result<()> {
ball.dir_x = -ball.dir_x;
ball.x = 0;
}
} else if ball.x >= (system.res.video.width() - BALL_WIDTH) as i32 {
} else {
if ball.x >= (system.res.video.width() - BALL_WIDTH) as i32 {
ball.dir_x = -ball.dir_x;
ball.x = (system.res.video.width() - BALL_WIDTH) as i32;
}
}
if ball.dir_y < 0 {
if ball.y <= 0 {
ball.dir_y = -ball.dir_y;
ball.y = 0;
}
} else if ball.y >= (system.res.video.height() - BALL_HEIGHT) as i32 {
} else {
if ball.y >= (system.res.video.height() - BALL_HEIGHT) as i32 {
ball.dir_y = -ball.dir_y;
ball.y = (system.res.video.height() - BALL_HEIGHT) as i32;
}
}
}
}
system.update()?;
system.res.video.clear(2);

View file

@ -4,5 +4,5 @@ version = "0.1.0"
edition = "2021"
[dependencies]
anyhow.workspace = true
anyhow = "=1.0.55"
ggdt = { path = "../../ggdt", features = [] }

Binary file not shown.

View file

@ -114,8 +114,8 @@ fn update_system_collision(context: &mut Context) {
let mut velocities = context.entities.components_mut::<Velocity>();
for (entity, _) in bounceables.iter() {
let position = positions.get_mut(entity).unwrap();
let velocity = velocities.get_mut(entity).unwrap();
let mut position = positions.get_mut(entity).unwrap();
let mut velocity = velocities.get_mut(entity).unwrap();
let mut bounced = false;
if position.0.x as i32 <= 0 || position.0.x as i32 + BALL_SIZE >= context.system.res.video.right() as i32 {

View file

@ -1,3 +1,5 @@
use std::path::Path;
use anyhow::Result;
use ggdt::prelude::*;

View file

@ -25,7 +25,7 @@ impl Game {
pub fn new(mut system: System<DosLike>) -> Result<Self> {
let font = BitmaskFont::new_vga_font()?;
let (balls_bmp, balls_palette) = IndexedBitmap::load_pcx_file("./assets/balls.pcx")?;
let (balls_bmp, balls_palette) = IndexedBitmap::load_pcx_file(Path::new("./assets/balls.pcx"))?;
system.res.palette = balls_palette.clone();
let mut sprites = Vec::new();

View file

@ -4,9 +4,9 @@ version = "0.1.0"
edition = "2021"
[dependencies]
anyhow.workspace = true
anyhow = "=1.0.55"
ggdt = { path = "../../ggdt" }
ggdt_imgui = { path = "../../ggdt_imgui" }
imgui.workspace = true
serde.workspace = true
serde_json.workspace = true
imgui = "0.11.0"
serde = { version = "1.0.136", features = ["derive"] }
serde_json = "1.0.79"

View file

@ -1,19 +1,17 @@
use std::collections::HashMap;
use std::rc::Rc;
use anyhow::Result;
use ggdt::prelude::*;
use crate::entities::{AnimationDef, EntityActivity, Event, SlimeColor};
use crate::entities::{AnimationDef, EntityActivity, Event};
use crate::support::{load_bitmap_atlas_autogrid, load_font, load_palette};
use crate::tilemap::TileMap;
use anyhow::Result;
use ggdt::prelude::*;
use std::collections::HashMap;
use std::path::Path;
use std::rc::Rc;
pub struct CoreContext {
pub delta: f32,
pub camera_x: i32,
pub camera_y: i32,
pub transparent_color: RGBA,
pub transparent_color: u32,
pub system: System<Standard>,
pub palette: Palette,
pub font: BitmaskFont,
@ -27,7 +25,6 @@ pub struct CoreContext {
pub tilemap: TileMap,
pub slime_activity_states: Rc<HashMap<EntityActivity, Rc<AnimationDef>>>,
pub sprite_render_list: Vec<(EntityId, Vector2, RgbaBlitMethod)>,
pub slime_texture_id_map: HashMap<SlimeColor, imgui::TextureId>,
}
impl CoreState<Standard> for CoreContext {
@ -90,31 +87,23 @@ impl AppContext<Standard> for GameContext {
impl GameContext {
pub fn new(system: System<Standard>) -> Result<Self> {
let palette = load_palette("./assets/db16.pal")?;
let palette = load_palette(Path::new("./assets/db16.pal"))?;
let font = load_font("./assets/dp.fnt")?;
let small_font = load_font("./assets/small.fnt")?;
let font = load_font(Path::new("./assets/dp.fnt"))?;
let small_font = load_font(Path::new("./assets/small.fnt"))?;
let tiles = Rc::new(load_bitmap_atlas_autogrid("./assets/tiles.pcx")?);
let green_slime = Rc::new(load_bitmap_atlas_autogrid("./assets/green_slime.pcx")?);
let blue_slime = Rc::new(load_bitmap_atlas_autogrid("./assets/blue_slime.pcx")?);
let orange_slime = Rc::new(load_bitmap_atlas_autogrid("./assets/orange_slime.pcx")?);
let tiles = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/tiles.pcx"))?);
let green_slime = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/green_slime.pcx"))?);
let blue_slime = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/blue_slime.pcx"))?);
let orange_slime = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/orange_slime.pcx"))?);
let tilemap = TileMap::load_from("./assets/arena.map.json")?;
let tilemap = TileMap::load_from(Path::new("./assets/arena.map.json"))?;
let entities = Entities::new();
let component_systems = ComponentSystems::new();
let event_publisher = EventPublisher::new();
let event_listeners = EventListeners::new();
let mut imgui = ggdt_imgui::ImGui::new();
let mut slime_texture_id_map = HashMap::new();
slime_texture_id_map
.insert(SlimeColor::Green, imgui.texture_map_mut().insert(green_slime.clone_tile(0).unwrap()));
slime_texture_id_map
.insert(SlimeColor::Blue, imgui.texture_map_mut().insert(blue_slime.clone_tile(0).unwrap()));
slime_texture_id_map
.insert(SlimeColor::Orange, imgui.texture_map_mut().insert(orange_slime.clone_tile(0).unwrap()));
let imgui = ggdt_imgui::ImGui::new();
let slime_activity_states = HashMap::from([
(EntityActivity::Idle, Rc::new(AnimationDef::new(&[1, 2], true, 1.0, Some(3)))),
@ -140,7 +129,6 @@ impl GameContext {
tilemap,
slime_activity_states: Rc::new(slime_activity_states),
sprite_render_list: Vec::new(),
slime_texture_id_map,
},
support: SupportContext { component_systems, event_listeners, imgui },
})

View file

@ -1,10 +1,8 @@
use std::collections::HashMap;
use std::rc::Rc;
use ggdt::prelude::*;
use crate::context::{CoreContext, GameContext};
use crate::tilemap::TileMap;
use ggdt::prelude::*;
use std::collections::HashMap;
use std::rc::Rc;
pub const FRICTION: f32 = 0.5;
pub const DEFAULT_PUSH_STRENGTH: f32 = 0.5;
@ -213,7 +211,7 @@ impl RandomlyWalksAround {
}
}
pub struct Slime(pub SlimeColor);
pub struct Slime;
pub struct Sprite {
pub atlas: Rc<BitmapAtlas<RgbaBitmap>>,
@ -369,7 +367,7 @@ pub fn remove_entity(entities: &mut Entities, entity: EntityId) {
pub fn set_entity_activity(entities: &mut Entities, entity: EntityId, new_activity: EntityActivity) {
let mut activities = entities.components_mut::<Activity>();
let activity = activities.get_mut(&entity).unwrap();
let mut activity = activities.get_mut(&entity).unwrap();
// only change the activity, and more importantly, the animation if we are actually applying
// an actual activity change from what it was before
@ -740,7 +738,7 @@ pub fn new_slime_entity(
let animate_by_activity = AnimateByActivity(context.slime_activity_states.clone());
let animation = AnimationInstance::from(animate_by_activity.0.get(&activity).unwrap().clone());
context.entities.add_component(id, Slime(color));
context.entities.add_component(id, Slime);
context.entities.add_component(id, Position(Vector2::new(x as f32, y as f32)));
context.entities.add_component(id, Velocity(Vector2::ZERO));
context.entities.add_component(id, Forces::new());

View file

@ -1,17 +1,16 @@
use anyhow::Result;
use ggdt::prelude::*;
use ggdt_imgui::UiSupport;
use crate::context::GameContext;
use crate::entities::{Forces, Position, Slime};
use crate::tilemap::{TILE_HEIGHT, TILE_WIDTH};
mod context;
mod entities;
mod support;
mod tilemap;
use anyhow::Result;
use crate::context::GameContext;
use crate::entities::{Forces, Position, Slime};
use crate::tilemap::{TILE_HEIGHT, TILE_WIDTH};
use ggdt::prelude::*;
use ggdt_imgui::UiSupport;
#[derive(Default)]
pub struct DemoState {
new_x: i32,
@ -28,22 +27,17 @@ impl AppState<GameContext> for DemoState {
let ui = context.support.imgui.new_frame(&context.core.system.res.video);
ui.window("Entities")
.position([10.0, 10.0], imgui::Condition::FirstUseEver)
.size([240.0, 200.0], imgui::Condition::FirstUseEver)
.size([200.0, 200.0], imgui::Condition::FirstUseEver)
.build(|| {
ui.text(format!("Camera: {}, {}", context.core.camera_x, context.core.camera_y));
ui.separator();
ui.text_colored([1.0, 1.0, 0.0, 1.0], "Slimes");
let mut positions = context.core.entities.components_mut::<Position>().unwrap();
for (slime, slime_type) in context.core.entities.components::<Slime>().unwrap().iter() {
for (slime, _) in context.core.entities.components::<Slime>().unwrap().iter() {
let position = positions.get(slime).unwrap();
ui.text(format!("{:2} @ {:3.0},{:3.0}", *slime, position.0.x, position.0.y));
if let Some(slime_type_texture_id) = context.core.slime_texture_id_map.get(&slime_type.0) {
ui.same_line();
ui.image("Slime Type", *slime_type_texture_id, [16.0, 16.0]);
}
ui.same_line();
let clicked = {
let _id = ui.push_id_ptr(slime);
@ -85,13 +79,12 @@ impl AppState<GameContext> for DemoState {
}
});
if !ui.is_any_hovered()
&& !ui.is_any_focused()
&& context.core.system.res.mouse.is_button_down(MouseButton::Right)
{
if !ui.is_any_hovered() && !ui.is_any_focused() {
if context.core.system.res.mouse.is_button_down(MouseButton::Right) {
context.core.camera_x -= context.core.system.res.mouse.x_delta() * 2;
context.core.camera_y -= context.core.system.res.mouse.y_delta() * 2;
}
}
context.support.do_events(&mut context.core);
context.support.component_systems.update(&mut context.core);

View file

@ -1,19 +1,17 @@
use std::path::Path;
use crate::tilemap::{TILE_HEIGHT, TILE_WIDTH};
use anyhow::{Context, Result};
use ggdt::prelude::*;
pub fn load_palette(path: impl AsRef<Path>) -> Result<Palette> {
Palette::load_from_file(&path, PaletteFormat::Vga).context(format!("Loading palette: {:?}", path.as_ref()))
pub fn load_palette(path: &std::path::Path) -> Result<Palette> {
Palette::load_from_file(path, PaletteFormat::Vga).context(format!("Loading palette: {:?}", path))
}
pub fn load_font(path: impl AsRef<Path>) -> Result<BitmaskFont> {
BitmaskFont::load_from_file(&path).context(format!("Loading font: {:?}", path.as_ref()))
pub fn load_font(path: &std::path::Path) -> Result<BitmaskFont> {
BitmaskFont::load_from_file(path).context(format!("Loading font: {:?}", path))
}
pub fn load_bitmap_atlas_autogrid(path: impl AsRef<Path>) -> Result<BitmapAtlas<RgbaBitmap>> {
let (bmp, _) = RgbaBitmap::load_file(&path).context(format!("Loading bitmap atlas: {:?}", path.as_ref()))?;
pub fn load_bitmap_atlas_autogrid(path: &std::path::Path) -> Result<BitmapAtlas<RgbaBitmap>> {
let (bmp, _) = RgbaBitmap::load_file(path).context(format!("Loading bitmap atlas: {:?}", path))?;
let mut atlas = BitmapAtlas::new(bmp);
atlas.add_grid(TILE_WIDTH, TILE_HEIGHT)?;
Ok(atlas)

View file

@ -1,5 +1,3 @@
use std::path::Path;
use anyhow::{Context, Result};
use ggdt::prelude::*;
@ -18,10 +16,10 @@ pub struct TileMap {
}
impl TileMap {
pub fn load_from(path: impl AsRef<Path>) -> Result<Self> {
let f = std::fs::File::open(&path)?;
pub fn load_from(path: &std::path::Path) -> Result<Self> {
let f = std::fs::File::open(path)?;
let reader = std::io::BufReader::new(f);
serde_json::from_reader(reader).context(format!("Loading json tilemap: {:?}", path.as_ref()))
serde_json::from_reader(reader).context(format!("Loading json tilemap: {:?}", path))
}
#[inline]
@ -65,7 +63,7 @@ impl TileMap {
tiles: &BitmapAtlas<RgbaBitmap>,
camera_x: i32,
camera_y: i32,
transparent_color: RGBA,
transparent_color: u32,
) {
let xt = camera_x / TILE_WIDTH as i32;
let yt = camera_y / TILE_HEIGHT as i32;

View file

@ -4,8 +4,8 @@ version = "0.1.0"
edition = "2021"
[dependencies]
anyhow.workspace = true
anyhow = "=1.0.55"
ggdt = { path = "../../ggdt", features = [] }
serde.workspace = true
serde_json.workspace = true
serde = { version = "1.0.136", features = ["derive"] }
serde_json = "1.0.79"

View file

@ -0,0 +1,9 @@
{
"width":40,
"height":30,
"layers":[
[96,96,96,96,96,96,96,96,96,96,96,96,16,17,16,16,16,16,16,17,17,32,16,16,16,16,16,32,16,16,16,16,16,16,16,16,16,33,16,16,96,96,96,96,96,96,96,96,96,96,96,16,17,17,17,32,17,32,16,16,32,16,16,32,16,16,16,16,16,16,32,16,33,16,16,16,16,16,16,16,96,96,96,96,96,96,96,96,96,96,181,178,178,178,178,183,32,16,17,181,178,178,178,178,178,178,178,178,178,178,178,183,16,16,16,16,16,16,16,32,96,96,96,96,96,96,96,96,96,181,195,16,32,17,17,193,178,178,178,195,16,16,16,16,16,16,32,16,16,16,16,193,178,183,16,32,16,16,16,16,96,96,96,96,96,96,96,181,178,195,16,16,16,32,17,17,17,17,32,16,16,16,33,16,16,16,16,16,16,16,16,16,16,193,183,16,16,16,33,16,96,96,96,96,96,96,181,195,32,16,16,16,16,16,16,16,32,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,32,16,193,183,16,16,16,16,16,17,16,16,96,181,195,16,16,33,16,16,16,16,16,32,16,16,16,16,16,32,16,16,16,16,48,48,48,48,16,16,16,16,16,196,16,16,16,16,32,16,17,16,181,195,16,16,16,16,16,32,16,16,16,16,16,16,16,16,16,16,16,16,48,48,48,48,48,48,48,48,16,16,32,193,183,16,32,16,8,8,8,181,195,16,16,16,16,17,32,16,16,16,16,16,16,16,16,16,16,16,16,48,48,48,48,48,48,48,48,48,48,16,16,16,196,16,16,16,7,7,7,196,16,32,16,32,17,17,17,16,16,16,16,33,16,16,16,16,16,16,48,48,48,48,48,48,48,48,48,48,48,48,16,16,193,183,16,32,7,7,181,195,16,16,16,16,16,32,16,16,16,16,16,16,16,16,16,32,16,16,16,48,48,48,48,48,48,48,48,48,48,48,48,32,16,196,32,16,7,7,196,16,16,32,16,16,32,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,48,48,48,48,48,48,48,48,48,48,16,16,16,196,16,16,7,7,196,8,8,16,16,16,16,16,16,32,16,16,16,16,32,16,16,16,16,16,16,16,16,48,48,48,48,48,48,48,48,16,16,16,16,196,17,16,7,7,196,7,7,16,16,16,16,16,16,16,16,16,33,16,16,16,16,16,16,16,16,16,32,16,16,48,48,48,48,48,16,16,16,16,16,196,17,16,7,7,196,7,7,16,16,16,16,16,33,16,16,16,16,16,16,16,16,16,32,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,196,17,16,16,16,196,16,33,16,16,16,16,16,16,16,16,16,16,16,32,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,196,32,16,16,16,196,16,16,16,16,16,32,16,16,16,16,32,16,16,16,32,16,16,16,16,16,16,16,16,32,16,16,16,16,16,16,16,16,16,16,196,16,16,16,16,196,32,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,32,16,16,16,16,16,16,16,33,16,16,16,16,16,32,16,196,16,33,32,16,196,16,16,16,16,32,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,33,16,16,16,16,16,16,16,16,16,16,16,16,16,196,32,16,16,16,196,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,32,16,16,16,16,16,196,16,16,16,16,193,183,16,16,48,48,48,48,48,16,16,16,16,16,32,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,196,32,16,32,16,33,196,48,48,48,48,48,48,48,48,16,16,16,16,16,16,16,16,32,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,196,16,16,16,16,16,193,183,48,48,48,48,48,48,48,48,16,16,32,16,16,16,16,16,16,16,16,33,16,16,16,16,16,16,16,16,16,16,16,16,196,16,16,16,32,16,16,196,48,48,48,48,48,48,48,48,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,32,16,196,16,16,16,16,16,16,193,183,48,48,48,48,48,48,48,48,48,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,181,195,16,16,16,16,16,16,16,193,183,48,48,48,48,48,48,48,48,48,48,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,196,16,16,16,16,16,16,16,16,16,193,178,178,183,48,48,48,48,48,48,48,48,16,16,16,16,16,32,16,16,16,16,32,16,16,16,16,16,16,181,195,16,16,16,32,16,16,33,16,32,16,16,16,193,178,178,178,178,178,178,178,178,183,16,32,16,16,16,16,16,33,16,16,16,16,16,16,181,178,195,32,16,16,16,16,16,16,16,16,16,16,32,16,16,16,16,16,16,16,33,32,17,193,178,178,178,178,178,178,178,178,178,178,178,178,178,178,195,16,16,16,16,16,48,16,16,32,16,16,16,16,16,16,16,16,16,16,32,16,16,16,16,32,16,16,32,16,16,16,32,16,16,16,33,16,16,32,16,16,16,16,16,48,48],
[-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,87,-1,104,108,108,108,105,-1,-1,-1,-1,-1,80,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,98,105,-1,-1,-1,26,-1,26,80,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,27,-1,-1,-1,80,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,80,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,80,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,266,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,80,-1,266,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,26,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,26,-1,-1,80,108,108,108,97,-1,-1,-1,-1,266,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,49,51,-1,-1,-1,-1,52,50,-1,-1,-1,-1,-1,-1,-1,-1,46,46,46,102,-1,-1,27,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,49,51,-1,-1,-1,-1,-1,-1,27,-1,52,50,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,51,-1,-1,26,-1,-1,-1,-1,-1,-1,27,52,50,-1,-1,-1,-1,-1,-1,-1,-1,-1,266,-1,-1,-1,-1,-1,-1,-1,-1,80,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,278,-1,-1,-1,-1,52,-1,-1,-1,26,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,26,-1,-1,-1,-1,67,-1,-1,27,279,-1,-1,294,-1,-1,-1,-1,-1,-1,-1,-1,-1,83,-1,-1,-1,46,46,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,65,67,-1,-1,-1,-1,-1,-1,26,-1,-1,266,68,-1,-1,-1,-1,86,-1,-1,-1,-1,-1,29,-1,-1,-1,-1,-1,-1,26,-1,-1,-1,-1,-1,-1,-1,-1,27,-1,65,67,-1,-1,-1,-1,-1,-1,-1,-1,68,66,-1,-1,-1,-1,86,-1,-1,-1,-1,-1,29,-1,27,-1,80,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,65,67,-1,-1,-1,-1,-1,68,66,-1,-1,26,-1,-1,86,-1,-1,-1,-1,-1,29,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,86,14,14,-1,14,14,-1,80,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,86,-1,282,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,80,-1,-1,-1,-1,104,-1,282,-1,-1,-1,259,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,26,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,26,-1,80,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,80,-1,281,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,49,51,-1,-1,-1,-1,-1,52,-1,-1,-1,-1,-1,-1,-1,80,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,80,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,52,-1,-1,-1,-1,-1,-1,-1,-1,-1,278,-1,-1,278,-1,-1,266,27,-1,278,-1,-1,-1,-1,26,-1,-1,-1,-1,-1,-1,-1,-1,26,-1,-1,-1,-1,-1,27,-1,50,-1,-1,-1,-1,-1,-1,-1,-1,294,-1,-1,294,-1,27,279,-1,-1,294,-1,-1,26,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,52,50,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,278,-1,-1,-1,-1,266,-1,-1,-1,-1,-1,-1,-1,-1,52,50,-1,-1,-1,-1,-1,-1,-1,-1,278,-1,-1,278,-1,-1,278,-1,-1,80,-1,-1,-1,-1,26,-1,294,-1,-1,-1,-1,-1,27,-1,-1,-1,-1,-1,-1,-1,-1,266,52,-1,-1,-1,27,279,-1,-1,294,-1,-1,294,-1,-1,294,-1,266,-1,-1,-1,26,-1,-1,-1,-1,-1,260,-1,-1,-1,-1,-1,-1,27,-1,-1,-1,26,-1,-1,-1,50,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,27,-1,-1,27,-1,49,-1,278,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,266,-1,-1,-1,-1,-1,-1,-1,27,-1,-1,-1,-1,-1,49,51,-1,294,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,27,-1,49,51,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,49,51,27,-1],
[-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,0,0,0,0,0,-1,-1,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,0,1,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,-1,-1,-1,-1,-1,-1,-1,-1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,-1,-1,-1,-1,-1,-1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,-1,-1,-1,-1,-1,-1,0,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,0,0,-1,-1,-1,-1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,-1,0,1,1,0,1,1,1,1,1,1,1,0,-1,-1,-1,-1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,-1,-1,-1,-1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,-1,-1,-1,-1,0,0,0,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,-1,-1,-1,-1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,-1,-1,-1,-1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,-1,-1,-1,-1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,-1,-1,-1,-1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,-1,-1,-1,-1,0,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,-1,-1,-1,-1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,-1,-1,-1,-1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,-1,-1,-1,-1,-1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,0,1,1,1,1,1,0,-1,-1,-1,-1,-1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,1,1,0,1,1,0,1,1,1,1,1,0,-1,-1,-1,-1,-1,-1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,-1,-1,-1,-1,-1,-1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,1,1,0,1,1,0,1,0,0,-1,-1,-1,-1,-1,-1,-1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,1,1,0,1,1,0,1,1,1,1,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1]
]
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -381,7 +381,7 @@ pub struct Pickuper;
pub fn init_everything(
context: &mut Game,
map_file: impl AsRef<Path>,
map_file: &Path,
min_spawn_time: f32,
max_spawn_time: f32,
max_slimes: usize,

View file

@ -105,7 +105,7 @@ fn move_entity_with_collision(
pub fn set_entity_activity(entities: &mut Entities, entity: EntityId, new_activity: EntityActivity) {
let mut activities = entities.components_mut::<Activity>();
let activity = activities.get_mut(&entity).unwrap();
let mut activity = activities.get_mut(&entity).unwrap();
// only change the activity, and more importantly, the animation if we are actually applying
// an actual activity change from what it was before
@ -215,7 +215,7 @@ pub fn attack(context: &mut Core, entity: EntityId) {
}
}
pub fn hit_entity(context: &mut Core, target: EntityId, _source: EntityId, damage: i32, damage_position: Vector2) {
pub fn hit_entity(context: &mut Core, target: EntityId, source: EntityId, damage: i32, damage_position: Vector2) {
let position;
{
let positions = context.entities.components::<Position>();
@ -248,15 +248,15 @@ pub fn stop_attack(context: &mut Core, entity: EntityId) {
remove_entity_attachment(&mut context.entities, entity);
}
pub fn pickup(context: &mut Core, _picked_up_by: EntityId, picked_up: EntityId) {
let _kind;
pub fn pickup(context: &mut Core, picked_up_by: EntityId, picked_up: EntityId) {
let kind;
let position;
{
let positions = context.entities.components::<Position>();
position = positions.get(&picked_up).unwrap().0;
let pickupables = context.entities.components::<Pickupable>();
_kind = pickupables.get(&picked_up).unwrap().kind;
kind = pickupables.get(&picked_up).unwrap().kind;
}
// TODO: tally up the kinds
@ -332,7 +332,7 @@ fn update_system_pushing(context: &mut Core) {
let pusher_bounds = bounds.get(pusher_entity).unwrap();
let pusher_circle = Circle::new(pusher_position.0.x as i32, pusher_position.0.y as i32, pusher_bounds.radius);
for (pushable_entity, _pushable) in pushable.iter() {
for (pushable_entity, pushable) in pushable.iter() {
// don't push ourself ...
if *pushable_entity == *pusher_entity {
continue;
@ -535,7 +535,7 @@ fn update_system_randomly_spawn_slimes(context: &mut Core) {
fn update_system_camera_follows_player(context: &mut Core) {
if let Some((player_entity, _)) = context.entities.components::<Player>().single() {
if let Some((_, camera)) = context.entities.components_mut::<Camera>().single_mut() {
if let Some((_, mut camera)) = context.entities.components_mut::<Camera>().single_mut() {
let positions = context.entities.components::<Position>().unwrap();
let position = positions.get(player_entity).unwrap();
@ -569,7 +569,7 @@ fn update_system_turn_attached_entities(context: &mut Core) {
// change the direction of the attachment (child) to match the parent ... if the
// attachment even has a direction itself ...
if let Some(facing_direction) = facing_directions.get_mut(&attachment.0) {
if let Some(mut facing_direction) = facing_directions.get_mut(&attachment.0) {
facing_direction.0 = parent_facing_direction;
}
}
@ -593,7 +593,7 @@ fn update_system_position_attached_entities(context: &mut Core) {
}
let attached_entity = attachment.0;
if let Some(attachment_position) = positions.get_mut(&attached_entity) {
if let Some(mut attachment_position) = positions.get_mut(&attached_entity) {
// start off the attachment by placing it directly at the parent
attachment_position.0 = parent_position;

View file

@ -1,6 +1,7 @@
extern crate core;
use std::collections::HashMap;
use std::path::Path;
use std::rc::Rc;
use anyhow::{Context, Result};
@ -107,30 +108,30 @@ impl AppContext<DosLike> for Game {
impl Game {
pub fn new(mut system: System<DosLike>) -> Result<Self> {
let palette = load_palette("./assets/db16.pal")?;
let palette = load_palette(Path::new("./assets/db16.pal"))?;
system.res.palette = palette.clone();
let font = load_font("./assets/dp.fnt")?;
let font = load_font(Path::new("./assets/dp.fnt"))?;
let tiles = Rc::new(load_bitmap_atlas_autogrid("./assets/tiles.pcx")?);
let hero_male = Rc::new(load_bitmap_atlas_autogrid("./assets/hero_male.pcx")?);
let hero_female = Rc::new(load_bitmap_atlas_autogrid("./assets/hero_female.pcx")?);
let green_slime = Rc::new(load_bitmap_atlas_autogrid("./assets/green_slime.pcx")?);
let blue_slime = Rc::new(load_bitmap_atlas_autogrid("./assets/blue_slime.pcx")?);
let orange_slime = Rc::new(load_bitmap_atlas_autogrid("./assets/orange_slime.pcx")?);
let fist = Rc::new(load_bitmap_atlas_autogrid("./assets/fist.pcx")?);
let sword = Rc::new(load_bitmap_atlas_autogrid("./assets/sword.pcx")?);
let particles = Rc::new(load_bitmap_atlas_autogrid("./assets/particles.pcx")?);
let items = Rc::new(load_bitmap_atlas_autogrid("./assets/items.pcx")?);
let tiles = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/tiles.pcx"))?);
let hero_male = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/hero_male.pcx"))?);
let hero_female = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/hero_female.pcx"))?);
let green_slime = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/green_slime.pcx"))?);
let blue_slime = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/blue_slime.pcx"))?);
let orange_slime = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/orange_slime.pcx"))?);
let fist = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/fist.pcx"))?);
let sword = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/sword.pcx"))?);
let particles = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/particles.pcx"))?);
let items = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/items.pcx"))?);
let mut ui = load_bitmap_atlas("./assets/ui.pcx")?;
let mut ui = load_bitmap_atlas(Path::new("./assets/ui.pcx"))?;
ui.add(Rect::new(0, 0, 16, 16))?;
ui.add(Rect::new(16, 0, 16, 16))?;
for i in 0..8 {
ui.add(Rect::new(i * 8, 16, 8, 8))?;
}
let tilemap = TileMap::load_from("./assets/title_screen.map.json")?;
let tilemap = TileMap::load_from(Path::new("./assets/title_screen.map.json"))?;
let entities = Entities::new();
let component_systems = ComponentSystems::new();

View file

@ -1,3 +1,5 @@
use std::path::Path;
use ggdt::prelude::*;
use crate::entities::*;
@ -43,7 +45,7 @@ impl AppState<Game> for MainMenuState {
None
}
fn render(&mut self, _state: State, context: &mut Game) {
fn render(&mut self, state: State, context: &mut Game) {
context.core.tilemap.draw(&mut context.core.system.res.video, &context.core.tiles, 0, 0);
context.support.component_systems.render(&mut context.core);
@ -83,10 +85,10 @@ impl AppState<Game> for MainMenuState {
update_fade_transition(state, &mut self.fade, context.core.delta * 3.0, context)
}
fn state_change(&mut self, new_state: State, _old_state: State, context: &mut Game) {
fn state_change(&mut self, new_state: State, old_state: State, context: &mut Game) {
match new_state {
State::Pending | State::Resume => {
init_everything(context, "./assets/title_screen.map.json", 0.2, 1.0, 32);
init_everything(context, Path::new("./assets/title_screen.map.json"), 0.2, 1.0, 32);
}
State::TransitionIn => {
self.fade = 0.0;
@ -170,7 +172,7 @@ impl AppState<Game> for GamePlayState {
None
}
fn render(&mut self, _state: State, context: &mut Game) {
fn render(&mut self, state: State, context: &mut Game) {
if let Some((_, camera)) = context.core.entities.components::<Camera>().single() {
context.core.tilemap.draw(&mut context.core.system.res.video, &context.core.tiles, camera.x, camera.y);
}
@ -214,10 +216,10 @@ impl AppState<Game> for GamePlayState {
update_fade_transition(state, &mut self.fade, context.core.delta * 3.0, context)
}
fn state_change(&mut self, new_state: State, _old_state: State, context: &mut Game) {
fn state_change(&mut self, new_state: State, old_state: State, context: &mut Game) {
match new_state {
State::Pending => {
init_everything(context, "./assets/arena.map.json", 0.5, 2.0, 100);
init_everything(context, Path::new("./assets/arena.map.json"), 0.5, 2.0, 100);
spawn_player_randomly(&mut context.core);
}
State::TransitionIn => {

View file

@ -6,23 +6,23 @@ use ggdt::prelude::*;
use crate::{Game, TILE_HEIGHT, TILE_WIDTH};
pub fn load_palette(path: impl AsRef<Path>) -> Result<Palette> {
Palette::load_from_file(&path, PaletteFormat::Vga).context(format!("Loading palette: {:?}", path.as_ref()))
pub fn load_palette(path: &Path) -> Result<Palette> {
Palette::load_from_file(path, PaletteFormat::Vga).context(format!("Loading palette: {:?}", path))
}
pub fn load_font(path: impl AsRef<Path>) -> Result<BitmaskFont> {
BitmaskFont::load_from_file(&path).context(format!("Loading font: {:?}", path.as_ref()))
pub fn load_font(path: &Path) -> Result<BitmaskFont> {
BitmaskFont::load_from_file(path).context(format!("Loading font: {:?}", path))
}
pub fn load_bitmap_atlas_autogrid(path: impl AsRef<Path>) -> Result<BitmapAtlas<IndexedBitmap>> {
let (bmp, _) = IndexedBitmap::load_file(&path).context(format!("Loading bitmap atlas: {:?}", path.as_ref()))?;
pub fn load_bitmap_atlas_autogrid(path: &Path) -> Result<BitmapAtlas<IndexedBitmap>> {
let (bmp, _) = IndexedBitmap::load_file(path).context(format!("Loading bitmap atlas: {:?}", path))?;
let mut atlas = BitmapAtlas::new(bmp);
atlas.add_grid(TILE_WIDTH, TILE_HEIGHT)?;
Ok(atlas)
}
pub fn load_bitmap_atlas(path: impl AsRef<Path>) -> Result<BitmapAtlas<IndexedBitmap>> {
let (bmp, _) = IndexedBitmap::load_file(&path).context(format!("Loading bitmap atlas: {:?}", path.as_ref()))?;
pub fn load_bitmap_atlas(path: &Path) -> Result<BitmapAtlas<IndexedBitmap>> {
let (bmp, _) = IndexedBitmap::load_file(path).context(format!("Loading bitmap atlas: {:?}", path))?;
let atlas = BitmapAtlas::new(bmp);
Ok(atlas)
}

View file

@ -21,10 +21,10 @@ pub struct TileMap {
}
impl TileMap {
pub fn load_from(path: impl AsRef<Path>) -> Result<Self> {
let f = File::open(&path)?;
pub fn load_from(path: &Path) -> Result<Self> {
let f = File::open(path)?;
let reader = BufReader::new(f);
serde_json::from_reader(reader).context(format!("Loading json tilemap: {:?}", path.as_ref()))
serde_json::from_reader(reader).context(format!("Loading json tilemap: {:?}", path))
}
#[inline]

View file

@ -4,5 +4,5 @@ version = "0.1.0"
edition = "2021"
[dependencies]
anyhow.workspace = true
anyhow = "=1.0.55"
ggdt = { path = "../../ggdt" }

View file

@ -108,16 +108,16 @@ impl AppState<App> for DemoState {
None
}
fn render(&mut self, _state: State, context: &mut App) {
fn render(&mut self, state: State, context: &mut App) {
context.core.system.res.video.clear(0);
context.support.component_systems.render(&mut context.core);
}
fn transition(&mut self, _state: State, _context: &mut App) -> bool {
fn transition(&mut self, state: State, context: &mut App) -> bool {
true
}
fn state_change(&mut self, new_state: State, _old_state: State, context: &mut App) {
fn state_change(&mut self, new_state: State, old_state: State, context: &mut App) {
match new_state {
State::Pending => {
self.init(context);

View file

@ -4,5 +4,5 @@ version = "0.1.0"
edition = "2021"
[dependencies]
anyhow.workspace = true
anyhow = "=1.0.55"
ggdt = { path = "../../ggdt" }

View file

@ -6,18 +6,16 @@ authors = ["Gered King <gered@blarg.ca>"]
edition = "2021"
[dependencies]
byte-slice-cast = "1.2.2"
byteorder = "1.5.0"
thiserror.workspace = true
byte-slice-cast = "1.2.1"
byteorder = "1.4.3"
thiserror = "=1.0.30"
rand = "0.8.5"
num = "0.4.1"
num-derive = "0.4.1"
num-traits = "0.2.17"
bitflags = "2.4.1"
flate2 = "=1.0.27"
num = "0.4.0"
num-derive = "0.3.3"
num-traits = "0.2.15"
bitflags = "2.1.0"
flate2 = "1.0.25"
crc32fast = "1.3.2"
serde.workspace = true
serde_json.workspace = true
[target.'cfg(not(windows))'.dependencies]
sdl2 = { git = "https://github.com/Rust-SDL2/rust-sdl2/", rev = "819ab438ac971a922d6ee1da558822002d343b4e", features = ["static-link", "bundled", "use-pkgconfig", "unsafe_textures"] }
@ -27,9 +25,9 @@ sdl2 = { git = "https://github.com/Rust-SDL2/rust-sdl2/", rev = "819ab438ac971a9
[dev-dependencies]
claim = "0.5.0"
criterion = "0.5.1"
anyhow.workspace = true
tempfile = "3.8.1"
criterion = "0.4.0"
anyhow = "=1.0.55"
tempfile = "3.4.0"
[[bench]]
name = "bitmap"
@ -43,19 +41,6 @@ harness = false
name = "loading"
harness = false
[[bench]]
name = "primitives"
harness = false
[[bench]]
name = "triangles"
harness = false
[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(recreate_ref_test_images)'] }
[lints.clippy]
too_long_first_doc_paragraph = "allow"
tabs_in_doc_comments = "allow" # fuck off
too_many_arguments = "allow"

View file

@ -7,10 +7,10 @@ pub fn criterion_benchmark(c: &mut Criterion) {
let height = 240;
let mut source = IndexedBitmap::new(width, height).unwrap();
let mut dest = vec![RGBA::default(); (width * height) 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();
c.bench_function("deindex_bitmap_pixels", |b| b.iter(|| source.copy_as_rgba_to(&mut dest, &palette)));
c.bench_function("deindex_bitmap_pixels", |b| b.iter(|| source.copy_as_argb_to(&mut dest, &palette)));
c.bench_function("set_pixel", |b| b.iter(|| source.set_pixel(black_box(100), black_box(100), black_box(42))));

View file

@ -1,10 +1,12 @@
use std::path::Path;
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use ggdt::prelude::*;
pub fn criterion_benchmark(c: &mut Criterion) {
let mut framebuffer = IndexedBitmap::new(320, 240).unwrap();
let (bmp, _) = IndexedBitmap::load_iff_file("./test-assets/test-tiles.lbm").unwrap();
let (bmp, _) = IndexedBitmap::load_iff_file(Path::new("./test-assets/test-tiles.lbm")).unwrap();
let mut solid_bmp = IndexedBitmap::new(16, 16).unwrap();
solid_bmp.blit_region(IndexedBlitMethod::Solid, &bmp, &Rect::new(16, 16, 16, 16), 0, 0);

View file

@ -1,163 +0,0 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use ggdt::prelude::*;
pub fn criterion_benchmark(c: &mut Criterion) {
let width = 320;
let height = 240;
const BG_COLOR: RGBA = RGBA::from_rgb([0, 0, 0]);
const SOLID_COLOR: RGBA = RGBA::from_rgb([255, 0, 255]);
const BLEND_COLOR: RGBA = RGBA::from_rgba([255, 0, 255, 127]);
let mut dest = RgbaBitmap::new(width, height).unwrap();
// note that none of these are all-inclusive benchmarks that cover all kinds of different scenarios and such
// where the rendering logic might change based on the actual arguments given (e.g. i am not benchmarking
// anything which does clipping as of yet).
// maybe i will eventually add that kind of detailed benchmarking, but this for now is to just get a very,
// very, VERY, basic idea about general performance ... moreso so that i can easily compare before/after
// as i change other things in the future
c.bench_function("rgbabitmap_primitives_set_pixel", |b| {
dest.clear(BG_COLOR);
b.iter(|| dest.set_pixel(black_box(100), black_box(100), black_box(SOLID_COLOR)))
});
c.bench_function("rgbabitmap_primitives_set_blended_pixel", |b| {
dest.clear(BG_COLOR);
b.iter(|| {
dest.set_blended_pixel(
black_box(100),
black_box(100),
black_box(BLEND_COLOR),
black_box(BlendFunction::Blend),
)
})
});
c.bench_function("rgbabitmap_primitives_set_pixel_unchecked", |b| {
dest.clear(BG_COLOR);
b.iter(|| unsafe { dest.set_pixel_unchecked(black_box(100), black_box(100), black_box(SOLID_COLOR)) })
});
c.bench_function("rgbabitmap_primitives_set_blended_pixel_unchecked", |b| {
dest.clear(BG_COLOR);
b.iter(|| unsafe {
dest.set_blended_pixel_unchecked(
black_box(100),
black_box(100),
black_box(BLEND_COLOR),
black_box(BlendFunction::Blend),
)
})
});
c.bench_function("rgbabitmap_primitives_line", |b| {
dest.clear(BG_COLOR);
b.iter(|| dest.line(black_box(10), black_box(50), black_box(310), black_box(120), black_box(SOLID_COLOR)))
});
c.bench_function("rgbabitmap_primitives_blended_line", |b| {
dest.clear(BG_COLOR);
b.iter(|| {
dest.blended_line(
black_box(10),
black_box(50),
black_box(310),
black_box(120),
black_box(BLEND_COLOR),
black_box(BlendFunction::Blend),
)
})
});
c.bench_function("rgbabitmap_primitives_horiz_line", |b| {
dest.clear(BG_COLOR);
b.iter(|| dest.horiz_line(black_box(10), black_box(310), black_box(70), black_box(SOLID_COLOR)))
});
c.bench_function("rgbabitmap_primitives_blended_horiz_line", |b| {
dest.clear(BG_COLOR);
b.iter(|| {
dest.blended_horiz_line(
black_box(10),
black_box(310),
black_box(70),
black_box(BLEND_COLOR),
black_box(BlendFunction::Blend),
)
})
});
c.bench_function("rgbabitmap_primitives_vert_line", |b| {
dest.clear(BG_COLOR);
b.iter(|| dest.vert_line(black_box(90), black_box(10), black_box(230), black_box(SOLID_COLOR)))
});
c.bench_function("rgbabitmap_primitives_blended_vert_line", |b| {
dest.clear(BG_COLOR);
b.iter(|| {
dest.blended_vert_line(
black_box(90),
black_box(10),
black_box(230),
black_box(BLEND_COLOR),
black_box(BlendFunction::Blend),
)
})
});
c.bench_function("rgbabitmap_primitives_rect", |b| {
dest.clear(BG_COLOR);
b.iter(|| dest.rect(black_box(10), black_box(10), black_box(310), black_box(230), black_box(SOLID_COLOR)))
});
c.bench_function("rgbabitmap_primitives_blended_rect", |b| {
dest.clear(BG_COLOR);
b.iter(|| {
dest.blended_rect(
black_box(10),
black_box(10),
black_box(310),
black_box(230),
black_box(BLEND_COLOR),
black_box(BlendFunction::Blend),
)
})
});
c.bench_function("rgbabitmap_primitives_filled_rect", |b| {
dest.clear(BG_COLOR);
b.iter(|| {
dest.filled_rect(black_box(10), black_box(10), black_box(310), black_box(230), black_box(SOLID_COLOR))
})
});
c.bench_function("rgbabitmap_primitives_blended_filled_rect", |b| {
dest.clear(BG_COLOR);
b.iter(|| {
dest.blended_filled_rect(
black_box(10),
black_box(10),
black_box(310),
black_box(230),
black_box(BLEND_COLOR),
black_box(BlendFunction::Blend),
)
})
});
c.bench_function("rgbabitmap_primitives_circle", |b| {
dest.clear(BG_COLOR);
b.iter(|| dest.circle(black_box(160), black_box(120), black_box(80), black_box(SOLID_COLOR)))
});
c.bench_function("rgbabitmap_primitives_filled_circle", |b| {
dest.clear(BG_COLOR);
b.iter(|| dest.filled_circle(black_box(160), black_box(120), black_box(80), black_box(SOLID_COLOR)))
});
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);

View file

@ -7,7 +7,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
let height = 240;
let mut dest = RgbaBitmap::new(width, height).unwrap();
let (texture, _) = RgbaBitmap::load_gif_file("./test-assets/gif/small.gif").unwrap();
let (texture, _) = RgbaBitmap::load_gif_file(std::path::Path::new("./test-assets/gif/small.gif")).unwrap();
let big_v1 = Vector2::new(47.0, 23.0);
let big_v2 = Vector2::new(60.0, 192.0);
@ -22,20 +22,19 @@ pub fn criterion_benchmark(c: &mut Criterion) {
let texcoord_0_1 = Vector2::new(0.0, 1.0);
let texcoord_1_1 = Vector2::new(1.0, 1.0);
let color = RGBA::from_rgb([255, 255, 255]);
let color_1 = RGBA::from_rgba([255, 0, 0, 255]);
let color_2 = RGBA::from_rgba([0, 255, 0, 255]);
let color_3 = RGBA::from_rgba([0, 0, 255, 255]);
let color_1 = to_argb32([255, 255, 0, 0]);
let color_2 = to_argb32([255, 0, 255, 0]);
let color_3 = to_argb32([255, 0, 0, 255]);
c.bench_function("rgbabitmap_triangle_2d_solid_color", |b| {
let triangle = RgbaTriangle2d::Solid { position: [big_v1, big_v2, big_v3], color };
let triangle = RgbaTriangle2d::Solid { position: [big_v1, big_v2, big_v3], color: 5 };
b.iter(|| {
dest.triangle_2d(black_box(&triangle));
})
});
c.bench_function("rgbabitmap_triangle_2d_solid_color_small", |b| {
let triangle = RgbaTriangle2d::Solid { position: [small_v1, small_v2, small_v3], color };
let triangle = RgbaTriangle2d::Solid { position: [small_v1, small_v2, small_v3], color: 5 };
b.iter(|| {
dest.triangle_2d(black_box(&triangle));
})

View file

@ -270,7 +270,7 @@ impl AudioBuffer {
/// Loads a WAV file into an [`AudioBuffer`]. The returned buffer will be in its original
/// format and may need to be converted before it can be played.
pub fn load_wav_file(path: impl AsRef<Path>) -> Result<AudioBuffer, WavError> {
pub fn load_wav_file(path: &Path) -> Result<AudioBuffer, WavError> {
let f = File::open(path)?;
let mut reader = BufReader::new(f);
Self::load_wav_bytes(&mut reader)
@ -287,31 +287,31 @@ mod tests {
const BASE_PATH: &str = "./test-assets/wav/";
fn test_file(file: impl AsRef<Path>) -> PathBuf {
fn test_file(file: &Path) -> PathBuf {
PathBuf::from(BASE_PATH).join(file)
}
#[test]
pub fn load_wav_file() -> Result<(), WavError> {
let wav_buffer = AudioBuffer::load_wav_file(test_file("22khz_8bit_1ch.wav").as_path())?;
let wav_buffer = AudioBuffer::load_wav_file(test_file(Path::new("22khz_8bit_1ch.wav")).as_path())?;
assert_eq!(AUDIO_FREQUENCY_22KHZ, wav_buffer.spec().frequency());
assert_eq!(1, wav_buffer.spec().channels());
assert_eq!(AudioFormat::U8, wav_buffer.spec.format);
assert_eq!(11248, wav_buffer.data.len());
let wav_buffer = AudioBuffer::load_wav_file(test_file("44khz_8bit_1ch.wav").as_path())?;
let wav_buffer = AudioBuffer::load_wav_file(test_file(Path::new("44khz_8bit_1ch.wav")).as_path())?;
assert_eq!(AUDIO_FREQUENCY_44KHZ, wav_buffer.spec().frequency());
assert_eq!(1, wav_buffer.spec().channels());
assert_eq!(AudioFormat::U8, wav_buffer.spec.format);
assert_eq!(22496, wav_buffer.data.len());
let wav_buffer = AudioBuffer::load_wav_file(test_file("22khz_16bit_1ch.wav").as_path())?;
let wav_buffer = AudioBuffer::load_wav_file(test_file(Path::new("22khz_16bit_1ch.wav")).as_path())?;
assert_eq!(AUDIO_FREQUENCY_22KHZ, wav_buffer.spec().frequency());
assert_eq!(1, wav_buffer.spec().channels());
assert_eq!(AudioFormat::S16LSB, wav_buffer.spec.format);
assert_eq!(22496, wav_buffer.data.len());
let wav_buffer = AudioBuffer::load_wav_file(test_file("44khz_16bit_1ch.wav").as_path())?;
let wav_buffer = AudioBuffer::load_wav_file(test_file(Path::new("44khz_16bit_1ch.wav")).as_path())?;
assert_eq!(AUDIO_FREQUENCY_44KHZ, wav_buffer.spec().frequency());
assert_eq!(1, wav_buffer.spec().channels());
assert_eq!(AudioFormat::S16LSB, wav_buffer.spec.format);

View file

@ -174,12 +174,6 @@ impl AudioChannel {
}
}
impl Default for AudioChannel {
fn default() -> Self {
Self::new()
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////
#[derive(Debug, Error)]

View file

@ -2,6 +2,8 @@ use sdl2::audio::{AudioFormat, AudioFormatNum, AudioSpecDesired};
use sdl2::AudioSubsystem;
use thiserror::Error;
use crate::audio::AudioDevice;
mod buffer;
mod device;
mod queue;

View file

@ -1,135 +1,133 @@
/*!
Optional, extra types and helpers that can be used to get game's main loop boilerplate up and
running quicker.
This is all of somewhat dubious quality and value at the moment. And it may continue to be this
way for a long while yet. And, truthfully, I suspect I may rip this out eventually. Maybe.
The very-long-winded rationale here is that as I've started building more and more things with
ggdt, I started implementing games/apps using a particular pattern which I was largely
pushed towards due to the Rust borrow-checker (as is often the case with Rust). My games/apps
needed to keep their state (for clarity, the word 'state' here is being used very broadly to
refer to all game/app state, and not just referring to the stuff inside `ggdt::states`)
somewhere and my needs were a bit complicated since my game/app state often included things
which needed to get passed other things from inside that same "bag" of state.
I originally wanted to do something like this, where this `App` struct is our overall "game/app
context" grab bag:
```
use ggdt::prelude::*;
pub enum Event { /* .. various events here .. */ }
struct App {
pub delta: f32,
pub system: System<DosLike>,
pub entities: Entities,
pub component_systems: ComponentSystems<App, App>, // oh no! :'(
pub event_publisher: EventPublisher<Event>,
pub event_listeners: EventListeners<Event, App>, // oh no again! :'(
}
```
Of course, we cannot do this, because then we end up trying to get additional mutable borrows
of `App` when we eventually try to call certain methods on either the `component_systems` or
`event_listeners` instances. Boooo! :-(
That of course lead me to split this structure up. I didn't and still don't like this because
I really don't know what to call these two things. They're both "context" and they're literally
only split up because of borrow-checker issues. But splitting them up did work for me. I
initially went with a parent-child split, which seemed logical to me at the time:
```
use ggdt::prelude::*;
pub enum Event { /* .. various events here .. */ }
// "core" because what the heck else do i call this? "InnerContext"? "InnerApp"? ...
struct Core {
pub delta: f32,
pub system: System<DosLike>,
pub entities: Entities,
pub event_publisher: EventPublisher<Event>,
}
// i guess this is a bit more obvious what to call it, but still ... doesn't sit right with me
struct App {
pub core: Core,
pub component_systems: ComponentSystems<Core, Core>,
pub event_listeners: EventListeners<Event, Core>,
}
```
This structure seemed to work generally well and I've gotten pretty far with it. Keeping the
main `ggdt::states::States` instance _separate_ was also key, and never really a problem
since that can (and should) just live at the top in your main loop. Easy.
I ended up with some common bits of code that I'd always add to projects using this structure,
such as a very simple copy+pasted main loop, as well as a very simple function that calculates
the new frame `delta` each iteration of the main loop. As well as event processing via the
`event_publisher` and `event_listener` instances. I also expect this set of common bits of code
to grow over time. And I, ideally, want a single place to put it all.
So, this module here is my attempt at trying to formalize this a bit more and do a bit of
refactoring where I can keep this common copy+pasted bits somewhere. As well, I decided to
move away from my "context" struct having a parent-child relation for the split of the data
kept in these, and instead just "flatten" it out a bit (sort of) as this seems much more
future-proof if/when I encounter more borrow-checker issues down the road with other additions
to these structures.
But again, better naming still eludes me here!
```
use ggdt::prelude::*;
pub enum Event { /* .. various events here .. */ }
// "Core" because it contains the things that probably 90% of game/app code will need to work
// with. you'd probably want to put your game/app resources/assets on this struct too.
struct Core {
pub delta: f32,
pub system: System<DosLike>,
pub entities: Entities,
pub event_publisher: EventPublisher<Event>,
}
// "Support" because it contains things that support the main/core game state?
// kinda grasping at straws here maybe ...
struct Support {
pub component_systems: ComponentSystems<Core, Core>,
pub event_listeners: EventListeners<Event, Core>,
}
// better, maybe?
struct App {
pub core: Core,
pub support: Support,
}
```
Even though it's another struct being added, I do like this more, despite the naming
uncertainty.
So, with this being my current preferred way to architect a ggdt-using project, I created
some traits here in this module to formalize this all a bit more. `CoreState` and (optionally)
`CoreStateWithEvents` are what you'd make your project's `Core` struct (as shown in the above
example code) implement, while `SupportSystems` and (optionally) `SupportSystemsWithEvents`
are what you'd make your project's `Support` struct (again, as shown in the above example code)
implement. Finally, `AppContext` is for your `App` struct that contains the two.
Once you have all this (which ironically ends up being _more_ code than if you'd not used these
traits ... heh), you can now optionally use the `main_loop` function to get a ready-to-use
main loop which is set up to use a `ggdt::states::State` state manager.
Having said all of this ... again, I will reiterate that I don't believe any of this has reached
anything resembling a "good design" ... yet. There may be a good design hidden somewhere in
here that I've yet to fully discover, but I definitely don't think I've arrived at quite it yet.
So, basically, I expect this to evolve over time (probably a _long_ time). And this is all
totally optional anyway.
*/
//! Optional, extra types and helpers that can be used to get game's main loop boilerplate up and
//! running quicker.
//!
//! This is all of somewhat dubious quality and value at the moment. And it may continue to be this
//! way for a long while yet. And, truthfully, I suspect I may rip this out eventually. Maybe.
//!
//! The very-long-winded rationale here is that as I've started building more and more things with
//! ggdt, I started implementing games/apps using a particular pattern which I was largely
//! pushed towards due to the Rust borrow-checker (as is often the case with Rust). My games/apps
//! needed to keep their state (for clarity, the word 'state' here is being used very broadly to
//! refer to all game/app state, and not just referring to the stuff inside `ggdt::states`)
//! somewhere and my needs were a bit complicated since my game/app state often included things
//! which needed to get passed other things from inside that same "bag" of state.
//!
//! I originally wanted to do something like this, where this `App` struct is our overall "game/app
//! context" grab bag:
//!
//! ```
//! use ggdt::prelude::*;
//!
//! pub enum Event { /* .. various events here .. */ }
//!
//! struct App {
//! pub delta: f32,
//! pub system: System<DosLike>,
//! pub entities: Entities,
//! pub component_systems: ComponentSystems<App, App>, // oh no! :'(
//! pub event_publisher: EventPublisher<Event>,
//! pub event_listeners: EventListeners<Event, App>, // oh no again! :'(
//! }
//! ```
//!
//! Of course, we cannot do this, because then we end up trying to get additional mutable borrows
//! of `App` when we eventually try to call certain methods on either the `component_systems` or
//! `event_listeners` instances. Boooo! :-(
//!
//! That of course lead me to split this structure up. I didn't and still don't like this because
//! I really don't know what to call these two things. They're both "context" and they're literally
//! only split up because of borrow-checker issues. But splitting them up did work for me. I
//! initially went with a parent-child split, which seemed logical to me at the time:
//!
//! ```
//! use ggdt::prelude::*;
//!
//! pub enum Event { /* .. various events here .. */ }
//!
//! // "core" because what the heck else do i call this? "InnerContext"? "InnerApp"? ...
//! struct Core {
//! pub delta: f32,
//! pub system: System<DosLike>,
//! pub entities: Entities,
//! pub event_publisher: EventPublisher<Event>,
//! }
//!
//! // i guess this is a bit more obvious what to call it, but still ... doesn't sit right with me
//! struct App {
//! pub core: Core,
//! pub component_systems: ComponentSystems<Core, Core>,
//! pub event_listeners: EventListeners<Event, Core>,
//! }
//! ```
//!
//! This structure seemed to work generally well and I've gotten pretty far with it. Keeping the
//! main `ggdt::states::States` instance _separate_ was also key, and never really a problem
//! since that can (and should) just live at the top in your main loop. Easy.
//!
//! I ended up with some common bits of code that I'd always add to projects using this structure,
//! such as a very simple copy+pasted main loop, as well as a very simple function that calculates
//! the new frame `delta` each iteration of the main loop. As well as event processing via the
//! `event_publisher` and `event_listener` instances. I also expect this set of common bits of code
//! to grow over time. And I, ideally, want a single place to put it all.
//!
//! So, this module here is my attempt at trying to formalize this a bit more and do a bit of
//! refactoring where I can keep this common copy+pasted bits somewhere. As well, I decided to
//! move away from my "context" struct having a parent-child relation for the split of the data
//! kept in these, and instead just "flatten" it out a bit (sort of) as this seems much more
//! future-proof if/when I encounter more borrow-checker issues down the road with other additions
//! to these structures.
//!
//! But again, better naming still eludes me here!
//!
//! ```
//! use ggdt::prelude::*;
//!
//! pub enum Event { /* .. various events here .. */ }
//!
//! // "Core" because it contains the things that probably 90% of game/app code will need to work
//! // with. you'd probably want to put your game/app resources/assets on this struct too.
//! struct Core {
//! pub delta: f32,
//! pub system: System<DosLike>,
//! pub entities: Entities,
//! pub event_publisher: EventPublisher<Event>,
//! }
//!
//! // "Support" because it contains things that support the main/core game state?
//! // kinda grasping at straws here maybe ...
//! struct Support {
//! pub component_systems: ComponentSystems<Core, Core>,
//! pub event_listeners: EventListeners<Event, Core>,
//! }
//!
//! // better, maybe?
//! struct App {
//! pub core: Core,
//! pub support: Support,
//! }
//! ```
//!
//! Even though it's another struct being added, I do like this more, despite the naming
//! uncertainty.
//!
//! So, with this being my current preferred way to architect a ggdt-using project, I created
//! some traits here in this module to formalize this all a bit more. `CoreState` and (optionally)
//! `CoreStateWithEvents` are what you'd make your project's `Core` struct (as shown in the above
//! example code) implement, while `SupportSystems` and (optionally) `SupportSystemsWithEvents`
//! are what you'd make your project's `Support` struct (again, as shown in the above example code)
//! implement. Finally, `AppContext` is for your `App` struct that contains the two.
//!
//! Once you have all this (which ironically ends up being _more_ code than if you'd not used these
//! traits ... heh), you can now optionally use the `main_loop` function to get a ready-to-use
//! main loop which is set up to use a `ggdt::states::State` state manager.
//!
//! Having said all of this ... again, I will reiterate that I don't believe any of this has reached
//! anything resembling a "good design" ... yet. There may be a good design hidden somewhere in
//! here that I've yet to fully discover, but I definitely don't think I've arrived at quite it yet.
//!
//! So, basically, I expect this to evolve over time (probably a _long_ time). And this is all
//! totally optional anyway.
//!
use thiserror::Error;

View file

@ -230,12 +230,6 @@ impl Entities {
}
}
impl Default for Entities {
fn default() -> Self {
Self::new()
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// TODO: is there some fancy way to get rid of the impl duplication here ... ?
@ -476,12 +470,6 @@ impl<U, R> ComponentSystems<U, R> {
}
}
impl<U, R> Default for ComponentSystems<U, R> {
fn default() -> Self {
Self::new()
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
#[cfg(test)]
@ -679,7 +667,7 @@ mod tests {
// modify position components
{
let mut positions = em.components_mut::<Position>().unwrap();
for component in positions.values_mut() {
for mut component in positions.values_mut() {
component.0 += 5;
}
@ -690,7 +678,7 @@ mod tests {
// modify health components
{
let mut healths = em.components_mut::<Health>().unwrap();
for component in healths.values_mut() {
for mut component in healths.values_mut() {
component.0 += 5;
}
assert_eq!(Health(25), *healths.get(&a).unwrap());
@ -734,10 +722,10 @@ mod tests {
println!("entity {}, health: {:?}, position: {:?}", name.0, health, position);
if let Some(health) = health {
if let Some(mut health) = health {
health.0 += 5;
}
if let Some(position) = position {
if let Some(mut position) = position {
position.0 += 5;
}
}

View file

@ -55,12 +55,6 @@ impl<EventType> EventPublisher<EventType> {
}
}
impl<EventType> Default for EventPublisher<EventType> {
fn default() -> Self {
Self::new()
}
}
/// A manager for application event listeners/handlers that can dispatch events queued up by a
/// [`EventPublisher`] to each of the event listeners/handlers registered with this manager.
///
@ -156,12 +150,6 @@ impl<EventType, ContextType> EventListeners<EventType, ContextType> {
}
}
impl<EventType, ContextType> Default for EventListeners<EventType, ContextType> {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
@ -187,12 +175,10 @@ mod tests {
}
fn dummy_listener(_event: &TestEvent, _context: &mut DummyContext) -> bool {
println!("dummy_listener event fired");
false
}
fn other_dummy_listener(_event: &TestEvent, _context: &mut DummyContext) -> bool {
println!("other_dummy_listener event fired");
false
}

View file

@ -127,10 +127,6 @@ fn get_flipped_blit_properties<PixelType: Pixel>(
(x_inc, src_start_x, src_start_y, src_next_row_inc)
}
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the source and destination bitmaps.
#[inline]
pub unsafe fn per_pixel_blit<PixelType: Pixel>(
dest: &mut Bitmap<PixelType>,
@ -157,10 +153,6 @@ pub unsafe fn per_pixel_blit<PixelType: Pixel>(
}
}
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the source and destination bitmaps.
#[inline]
pub unsafe fn per_pixel_flipped_blit<PixelType: Pixel>(
dest: &mut Bitmap<PixelType>,
@ -191,10 +183,6 @@ pub unsafe fn per_pixel_flipped_blit<PixelType: Pixel>(
}
}
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the source and destination bitmaps.
#[inline]
pub unsafe fn per_pixel_rotozoom_blit<PixelType: Pixel>(
dest: &mut Bitmap<PixelType>,
@ -292,10 +280,6 @@ pub unsafe fn per_pixel_rotozoom_blit<PixelType: Pixel>(
}
impl<PixelType: Pixel> Bitmap<PixelType> {
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the source and destination bitmaps.
pub unsafe fn solid_blit(&mut self, src: &Self, src_region: &Rect, dest_x: i32, dest_y: i32) {
let src_row_length = src_region.width as usize;
let src_pitch = src.width as usize;
@ -310,10 +294,6 @@ impl<PixelType: Pixel> Bitmap<PixelType> {
}
}
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the source and destination bitmaps.
pub unsafe fn solid_flipped_blit(
&mut self,
src: &Self,
@ -337,10 +317,6 @@ impl<PixelType: Pixel> Bitmap<PixelType> {
);
}
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the source and destination bitmaps.
pub unsafe fn transparent_blit(
&mut self,
src: &Self,
@ -363,10 +339,6 @@ impl<PixelType: Pixel> Bitmap<PixelType> {
);
}
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the source and destination bitmaps.
pub unsafe fn transparent_flipped_blit(
&mut self,
src: &Self,
@ -393,10 +365,6 @@ impl<PixelType: Pixel> Bitmap<PixelType> {
);
}
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the source and destination bitmaps.
pub unsafe fn transparent_single_color_blit(
&mut self,
src: &Self,
@ -420,10 +388,6 @@ impl<PixelType: Pixel> Bitmap<PixelType> {
);
}
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the source and destination bitmaps.
pub unsafe fn transparent_flipped_single_color_blit(
&mut self,
src: &Self,
@ -451,10 +415,6 @@ impl<PixelType: Pixel> Bitmap<PixelType> {
);
}
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the source and destination bitmaps.
pub unsafe fn rotozoom_blit(
&mut self,
src: &Self,
@ -480,10 +440,6 @@ impl<PixelType: Pixel> Bitmap<PixelType> {
);
}
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the source and destination bitmaps.
pub unsafe fn rotozoom_transparent_blit(
&mut self,
src: &Self,

View file

@ -7,7 +7,7 @@
//! Only a subset of the most common Bitmap drawing operations will be provided here.
use crate::graphics::{
BitmapError, Font, FontRenderOpts, IndexedBitmap, IndexedBlitMethod, Pixel, RgbaBitmap, RgbaBlitMethod, RGBA,
BitmapError, Font, FontRenderOpts, IndexedBitmap, IndexedBlitMethod, Pixel, RgbaBitmap, RgbaBlitMethod,
};
use crate::math::Rect;
@ -220,7 +220,7 @@ impl GeneralBitmap for IndexedBitmap {
}
impl GeneralBitmap for RgbaBitmap {
type PixelType = RGBA;
type PixelType = u32;
#[inline]
fn new(width: u32, height: u32) -> Result<Self, BitmapError> {

View file

@ -468,7 +468,7 @@ impl IndexedBitmap {
Ok((bitmap.unwrap(), palette.unwrap()))
}
pub fn load_gif_file(path: impl AsRef<Path>) -> Result<(IndexedBitmap, Palette), GifError> {
pub fn load_gif_file(path: &Path) -> Result<(IndexedBitmap, Palette), GifError> {
let f = File::open(path)?;
let mut reader = BufReader::new(f);
Self::load_gif_bytes(&mut reader)
@ -520,12 +520,7 @@ impl IndexedBitmap {
Ok(())
}
pub fn to_gif_file(
&self,
path: impl AsRef<Path>,
palette: &Palette,
settings: GifSettings,
) -> Result<(), GifError> {
pub fn to_gif_file(&self, path: &Path, palette: &Palette, settings: GifSettings) -> Result<(), GifError> {
let f = File::create(path)?;
let mut writer = BufWriter::new(f);
self.to_gif_bytes(&mut writer, palette, settings)
@ -542,7 +537,7 @@ impl RgbaBitmap {
Ok((output, palette))
}
pub fn load_gif_file(path: impl AsRef<Path>) -> Result<(RgbaBitmap, Palette), GifError> {
pub fn load_gif_file(path: &Path) -> Result<(RgbaBitmap, Palette), GifError> {
let (temp_bitmap, palette) = IndexedBitmap::load_gif_file(path)?;
let output = temp_bitmap.to_rgba(&palette);
Ok((output, palette))
@ -561,7 +556,7 @@ mod tests {
const BASE_PATH: &str = "./test-assets/gif/";
fn test_file(file: impl AsRef<Path>) -> PathBuf {
fn test_file(file: &Path) -> PathBuf {
PathBuf::from(BASE_PATH).join(file)
}
@ -569,14 +564,14 @@ mod tests {
fn load_and_save() -> Result<(), GifError> {
let tmp_dir = TempDir::new()?;
let ref_pixels = load_raw_indexed(test_file("small.bin"))?;
let ref_pixels = load_raw_indexed(test_file(Path::new("small.bin")).as_path())?;
let dp2_palette = Palette::load_from_file(
test_assets_file("dp2.pal"), //
test_assets_file(Path::new("dp2.pal")).as_path(), //
PaletteFormat::Normal,
)
.unwrap();
let (bmp, palette) = IndexedBitmap::load_gif_file(test_file("small.gif"))?;
let (bmp, palette) = IndexedBitmap::load_gif_file(test_file(Path::new("small.gif")).as_path())?;
assert_eq!(16, bmp.width());
assert_eq!(16, bmp.height());
assert_eq!(bmp.pixels(), ref_pixels.as_ref());
@ -602,9 +597,9 @@ mod tests {
// first image
let ref_pixels = load_raw_indexed(test_file("large_1.bin"))?;
let ref_pixels = load_raw_indexed(test_file(Path::new("large_1.bin")).as_path())?;
let (bmp, palette) = IndexedBitmap::load_gif_file(test_file("large_1.gif"))?;
let (bmp, palette) = IndexedBitmap::load_gif_file(test_file(Path::new("large_1.gif")).as_path())?;
assert_eq!(320, bmp.width());
assert_eq!(200, bmp.height());
assert_eq!(bmp.pixels(), ref_pixels.as_ref());
@ -618,9 +613,9 @@ mod tests {
// second image
let ref_pixels = load_raw_indexed(test_file("large_2.bin"))?;
let ref_pixels = load_raw_indexed(test_file(Path::new("large_2.bin")).as_path())?;
let (bmp, palette) = IndexedBitmap::load_gif_file(test_file("large_2.gif"))?;
let (bmp, palette) = IndexedBitmap::load_gif_file(test_file(Path::new("large_2.gif")).as_path())?;
assert_eq!(320, bmp.width());
assert_eq!(200, bmp.height());
assert_eq!(bmp.pixels(), ref_pixels.as_ref());

View file

@ -213,7 +213,7 @@ fn merge_bitplane(plane: u32, src: &[u8], dest: &mut [u8], row_size: usize) {
fn extract_bitplane(plane: u32, src: &[u8], dest: &mut [u8], row_size: usize) {
let bitmask = 1 << plane;
let mut src_base_index = 0;
for dest_pixel in dest.iter_mut().take(row_size) {
for x in 0..row_size {
let mut data = 0;
if src[src_base_index] & bitmask != 0 {
data |= 128;
@ -241,7 +241,7 @@ fn extract_bitplane(plane: u32, src: &[u8], dest: &mut [u8], row_size: usize) {
}
src_base_index += 8;
*dest_pixel = data;
dest[x] = data;
}
}
@ -430,7 +430,7 @@ impl IndexedBitmap {
Ok((bitmap.unwrap(), palette.unwrap()))
}
pub fn load_iff_file(path: impl AsRef<Path>) -> Result<(IndexedBitmap, Palette), IffError> {
pub fn load_iff_file(path: &Path) -> Result<(IndexedBitmap, Palette), IffError> {
let f = File::open(path)?;
let mut reader = BufReader::new(f);
Self::load_iff_bytes(&mut reader)
@ -522,7 +522,7 @@ impl IndexedBitmap {
Ok(())
}
pub fn to_iff_file(&self, path: impl AsRef<Path>, palette: &Palette, format: IffFormat) -> Result<(), IffError> {
pub fn to_iff_file(&self, path: &Path, palette: &Palette, format: IffFormat) -> Result<(), IffError> {
let f = File::create(path)?;
let mut writer = BufWriter::new(f);
self.to_iff_bytes(&mut writer, palette, format)
@ -539,7 +539,7 @@ impl RgbaBitmap {
Ok((output, palette))
}
pub fn load_iff_file(path: impl AsRef<Path>) -> Result<(RgbaBitmap, Palette), IffError> {
pub fn load_iff_file(path: &Path) -> Result<(RgbaBitmap, Palette), IffError> {
let (temp_bitmap, palette) = IndexedBitmap::load_iff_file(path)?;
let output = temp_bitmap.to_rgba(&palette);
Ok((output, palette))
@ -558,7 +558,7 @@ mod tests {
const BASE_PATH: &str = "./test-assets/iff/";
fn test_file(file: impl AsRef<Path>) -> PathBuf {
fn test_file(file: &Path) -> PathBuf {
PathBuf::from(BASE_PATH).join(file)
}
@ -566,16 +566,16 @@ mod tests {
pub fn load_and_save() -> Result<(), IffError> {
let tmp_dir = TempDir::new()?;
let ref_pixels = load_raw_indexed(test_file("small.bin"))?;
let ref_pixels = load_raw_indexed(test_file(Path::new("small.bin")).as_path())?;
let dp2_palette = Palette::load_from_file(
test_assets_file("dp2.pal"), //
test_assets_file(Path::new("dp2.pal")).as_path(), //
PaletteFormat::Normal,
)
.unwrap();
// ILBM format
let (bmp, palette) = IndexedBitmap::load_iff_file(test_file("small.lbm"))?;
let (bmp, palette) = IndexedBitmap::load_iff_file(test_file(Path::new("small.lbm")).as_path())?;
assert_eq!(16, bmp.width());
assert_eq!(16, bmp.height());
assert_eq!(bmp.pixels(), ref_pixels.as_ref());
@ -591,7 +591,7 @@ mod tests {
// PBM format
let (bmp, palette) = IndexedBitmap::load_iff_file(test_file("small.pbm"))?;
let (bmp, palette) = IndexedBitmap::load_iff_file(test_file(Path::new("small.pbm")).as_path())?;
assert_eq!(16, bmp.width());
assert_eq!(16, bmp.height());
assert_eq!(bmp.pixels(), ref_pixels.as_ref());
@ -614,9 +614,9 @@ mod tests {
// first image, PBM format
let ref_pixels = load_raw_indexed(test_file("large_1.bin"))?;
let ref_pixels = load_raw_indexed(test_file(Path::new("large_1.bin")).as_path())?;
let (bmp, palette) = IndexedBitmap::load_iff_file(test_file("large_1.pbm"))?;
let (bmp, palette) = IndexedBitmap::load_iff_file(test_file(Path::new("large_1.pbm")).as_path())?;
assert_eq!(320, bmp.width());
assert_eq!(200, bmp.height());
assert_eq!(bmp.pixels(), ref_pixels.as_ref());
@ -630,7 +630,7 @@ mod tests {
// first image, ILBM format
let (bmp, palette) = IndexedBitmap::load_iff_file(test_file("large_1.lbm"))?;
let (bmp, palette) = IndexedBitmap::load_iff_file(test_file(Path::new("large_1.lbm")).as_path())?;
assert_eq!(320, bmp.width());
assert_eq!(200, bmp.height());
assert_eq!(bmp.pixels(), ref_pixels.as_ref());
@ -644,9 +644,9 @@ mod tests {
// second image, PBM format
let ref_pixels = load_raw_indexed(test_file("large_2.bin"))?;
let ref_pixels = load_raw_indexed(test_file(Path::new("large_2.bin")).as_path())?;
let (bmp, palette) = IndexedBitmap::load_iff_file(test_file("large_2.lbm"))?;
let (bmp, palette) = IndexedBitmap::load_iff_file(test_file(Path::new("large_2.lbm")).as_path())?;
assert_eq!(320, bmp.width());
assert_eq!(200, bmp.height());
assert_eq!(bmp.pixels(), ref_pixels.as_ref());
@ -660,7 +660,7 @@ mod tests {
// second image, ILBM format
let (bmp, palette) = IndexedBitmap::load_iff_file(test_file("large_2.lbm"))?;
let (bmp, palette) = IndexedBitmap::load_iff_file(test_file(Path::new("large_2.lbm")).as_path())?;
assert_eq!(320, bmp.width());
assert_eq!(200, bmp.height());
assert_eq!(bmp.pixels(), ref_pixels.as_ref());

View file

@ -126,10 +126,6 @@ pub enum IndexedBlitMethod {
}
impl IndexedBitmap {
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the source and destination bitmaps.
pub unsafe fn solid_blended_blit(
&mut self,
src: &Self,
@ -154,10 +150,6 @@ impl IndexedBitmap {
);
}
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the source and destination bitmaps.
pub unsafe fn solid_flipped_blended_blit(
&mut self,
src: &Self,
@ -186,10 +178,6 @@ impl IndexedBitmap {
);
}
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the source and destination bitmaps.
pub unsafe fn solid_palette_offset_blit(
&mut self,
src: &Self,
@ -210,10 +198,6 @@ impl IndexedBitmap {
);
}
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the source and destination bitmaps.
pub unsafe fn solid_flipped_palette_offset_blit(
&mut self,
src: &Self,
@ -238,10 +222,6 @@ impl IndexedBitmap {
);
}
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the source and destination bitmaps.
pub unsafe fn transparent_blended_blit(
&mut self,
src: &Self,
@ -269,10 +249,6 @@ impl IndexedBitmap {
);
}
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the source and destination bitmaps.
pub unsafe fn transparent_flipped_blended_blit(
&mut self,
src: &Self,
@ -304,10 +280,6 @@ impl IndexedBitmap {
);
}
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the source and destination bitmaps.
pub unsafe fn transparent_palette_offset_blit(
&mut self,
src: &Self,
@ -331,10 +303,6 @@ impl IndexedBitmap {
);
}
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the source and destination bitmaps.
pub unsafe fn transparent_flipped_palette_offset_blit(
&mut self,
src: &Self,
@ -362,10 +330,6 @@ impl IndexedBitmap {
);
}
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the source and destination bitmaps.
pub unsafe fn rotozoom_blended_blit(
&mut self,
src: &Self,
@ -399,10 +363,6 @@ impl IndexedBitmap {
);
}
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the source and destination bitmaps.
pub unsafe fn rotozoom_transparent_blended_blit(
&mut self,
src: &Self,
@ -439,10 +399,6 @@ impl IndexedBitmap {
);
}
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the source and destination bitmaps.
pub unsafe fn rotozoom_palette_offset_blit(
&mut self,
src: &Self,
@ -470,10 +426,6 @@ impl IndexedBitmap {
);
}
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the source and destination bitmaps.
pub unsafe fn rotozoom_transparent_palette_offset_blit(
&mut self,
src: &Self,
@ -570,10 +522,6 @@ impl IndexedBitmap {
};
}
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the source and destination bitmaps.
#[inline]
#[rustfmt::skip]
pub unsafe fn blit_region_unchecked(
@ -658,20 +606,12 @@ impl IndexedBitmap {
}
}
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the source and destination bitmaps.
#[inline]
pub unsafe fn blit_unchecked(&mut self, method: IndexedBlitMethod, src: &Self, x: i32, y: i32) {
let src_region = Rect::new(0, 0, src.width, src.height);
self.blit_region_unchecked(method, src, &src_region, x, y);
}
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the source and destination bitmaps.
#[inline]
pub unsafe fn blit_atlas_unchecked(
&mut self,

View file

@ -1,6 +1,6 @@
use std::path::Path;
use crate::graphics::{Bitmap, BitmapError, Palette, RgbaBitmap, RGBA};
use crate::graphics::{Bitmap, BitmapError, Palette, RgbaBitmap};
mod blit;
mod primitives;
@ -25,8 +25,8 @@ impl IndexedBitmap {
Self::internal_new(width, height, 0)
}
pub fn load_file(path: impl AsRef<Path>) -> Result<(Self, Palette), BitmapError> {
if let Some(extension) = path.as_ref().extension() {
pub fn load_file(path: &Path) -> Result<(Self, Palette), BitmapError> {
if let Some(extension) = path.extension() {
let extension = extension.to_ascii_lowercase();
match extension.to_str() {
Some("png") => {
@ -44,14 +44,14 @@ impl IndexedBitmap {
}
/// Copies and converts the entire pixel data from this bitmap to a destination expecting
/// 32-bit RGBA-format pixel data. This can be used to display the contents of the bitmap
/// 32-bit ARGB-format pixel data. This can be used to display the contents of the bitmap
/// on-screen by using an SDL Surface, OpenGL texture, etc as the destination.
///
/// # Arguments
///
/// * `dest`: destination 32-bit RGBA pixel buffer to copy converted pixels to
/// * `dest`: destination 32-bit ARGB pixel buffer to copy converted pixels to
/// * `palette`: the 256 colour palette to use during pixel conversion
pub fn copy_as_rgba_to(&self, dest: &mut [RGBA], palette: &Palette) {
pub fn copy_as_argb_to(&self, dest: &mut [u32], palette: &Palette) {
for (src, dest) in self.pixels().iter().zip(dest.iter_mut()) {
*dest = palette[*src];
}
@ -67,7 +67,7 @@ impl IndexedBitmap {
/// returns: `RgbaBitmap`
pub fn to_rgba(&self, palette: &Palette) -> RgbaBitmap {
let mut output = RgbaBitmap::new(self.width, self.height).unwrap();
self.copy_as_rgba_to(output.pixels_mut(), palette);
self.copy_as_argb_to(output.pixels_mut(), palette);
output
}
}

View file

@ -20,11 +20,8 @@ impl IndexedBitmap {
}
/// Sets the pixel at the given coordinates using a blended color via the specified blend map,
/// or using the color specified if the blend map does not include the given color.
///
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// or using the color specified if the blend map does not include the given color. The
/// coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the bitmap.
#[inline]
pub unsafe fn set_blended_pixel_unchecked(&mut self, x: i32, y: i32, color: u8, blend_map: &BlendMap) {
@ -43,7 +40,6 @@ impl IndexedBitmap {
/// Draws a line from x1,y1 to x2,y2 by blending the drawn pixels using the given blend map,
/// or the color specified if the blend map does not include this color.
#[inline]
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) {
self.line_custom(
@ -60,7 +56,6 @@ impl IndexedBitmap {
/// Draws a horizontal line from x1,y to x2,y by blending the drawn pixels using the given
/// blend map, or the color specified if the blend map does not include this color.
#[inline]
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) {
self.horiz_line_custom(
@ -76,7 +71,6 @@ impl IndexedBitmap {
/// Draws a vertical line from x,y1 to x,y2 by blending the drawn pixels using the given blend
/// map, or the color specified if the blend map does not include this color.
#[inline]
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) {
self.vert_line_custom(
@ -94,7 +88,6 @@ impl IndexedBitmap {
/// drawn, assuming they are specifying the top-left and bottom-right corners respectively.
/// The box is drawn by blending the drawn pixels using the given blend map, or the color
/// specified if the blend map does not include this color.
#[inline]
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) {
self.rect_custom(
@ -113,7 +106,6 @@ impl IndexedBitmap {
/// drawn, assuming they are specifying the top-left and bottom-right corners respectively. The
/// filled box is draw by blending the drawn pixels using the given blend map, or the color
/// specified if the blend map does not include this color.
#[inline]
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) {
self.filled_rect_custom(

View file

@ -89,7 +89,7 @@ impl IndexedBitmap {
use IndexedTriangle2d::*;
match triangle {
Solid { position, color } => self.solid_triangle_2d(position, *color),
SolidBlended { position, color, blendmap } => self.solid_blended_triangle_2d(position, *color, blendmap),
SolidBlended { position, color, blendmap } => self.solid_blended_triangle_2d(position, *color, *blendmap),
SolidTextured { position, texcoord, bitmap } => self.solid_textured_triangle_2d(position, texcoord, bitmap),
SolidTexturedBlended { position, texcoord, bitmap, blendmap } => {
self.solid_textured_blended_triangle_2d(position, texcoord, bitmap, blendmap)

View file

@ -212,12 +212,8 @@ impl<PixelType: Pixel> Bitmap<PixelType> {
}
/// Returns an unsafe reference to the subset of the raw pixels in this bitmap beginning at the
/// given coordinates and extending to the end of the bitmap.
///
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the bitmap.
/// given coordinates and extending to the end of the bitmap. The coordinates are not checked
/// for validity, so it is up to you to ensure they lie within the bounds of the bitmap.
#[inline]
pub unsafe fn pixels_at_unchecked(&self, x: i32, y: i32) -> &[PixelType] {
let offset = self.get_offset_to_xy(x, y);
@ -225,12 +221,8 @@ impl<PixelType: Pixel> Bitmap<PixelType> {
}
/// Returns a mutable unsafe reference to the subset of the raw pixels in this bitmap beginning
/// at the given coordinates and extending to the end of the bitmap.
///
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the bitmap.
/// at the given coordinates and extending to the end of the bitmap. The coordinates are not
/// checked for validity, so it is up to you to ensure they lie within the bounds of the bitmap.
#[inline]
pub unsafe fn pixels_at_mut_unchecked(&mut self, x: i32, y: i32) -> &mut [PixelType] {
let offset = self.get_offset_to_xy(x, y);
@ -244,10 +236,10 @@ impl<PixelType: Pixel> Bitmap<PixelType> {
/// coordinates. If the coordinates given are outside the bitmap's current clipping region,
/// None is returned.
#[inline]
pub fn pixels_at_ptr(&self, x: i32, y: i32) -> Option<*const PixelType> {
pub unsafe fn pixels_at_ptr(&self, x: i32, y: i32) -> Option<*const PixelType> {
if self.is_xy_visible(x, y) {
let offset = self.get_offset_to_xy(x, y);
Some(unsafe { self.pixels.as_ptr().add(offset) })
Some(self.pixels.as_ptr().add(offset))
} else {
None
}
@ -257,22 +249,18 @@ impl<PixelType: Pixel> Bitmap<PixelType> {
/// given coordinates. If the coordinates given are outside the bitmap's current clipping
/// region, None is returned.
#[inline]
pub fn pixels_at_mut_ptr(&mut self, x: i32, y: i32) -> Option<*mut PixelType> {
pub unsafe fn pixels_at_mut_ptr(&mut self, x: i32, y: i32) -> Option<*mut PixelType> {
if self.is_xy_visible(x, y) {
let offset = self.get_offset_to_xy(x, y);
Some(unsafe { self.pixels.as_mut_ptr().add(offset) })
Some(self.pixels.as_mut_ptr().add(offset))
} else {
None
}
}
/// Returns an unsafe pointer to the subset of the raw pixels in this bitmap beginning at the
/// given coordinates.
///
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the bitmap.
/// given coordinates. The coordinates are not checked for validity, so it is up to you to
/// ensure they lie within the bounds of the bitmap.
#[inline]
pub unsafe fn pixels_at_ptr_unchecked(&self, x: i32, y: i32) -> *const PixelType {
let offset = self.get_offset_to_xy(x, y);
@ -280,12 +268,8 @@ impl<PixelType: Pixel> Bitmap<PixelType> {
}
/// Returns a mutable unsafe pointer to the subset of the raw pixels in this bitmap beginning
/// at the given coordinates.
///
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the bitmap.
/// at the given coordinates. The coordinates are not checked for validity, so it is up to you
/// to ensure they lie within the bounds of the bitmap.
#[inline]
pub unsafe fn pixels_at_mut_ptr_unchecked(&mut self, x: i32, y: i32) -> *mut PixelType {
let offset = self.get_offset_to_xy(x, y);
@ -498,15 +482,15 @@ mod tests {
let mut bmp = Bitmap::<u8>::new(8, 8).unwrap();
bmp.pixels_mut().copy_from_slice(RAW_BMP_PIXELS);
assert_eq!(None, bmp.pixels_at_ptr(-1, -1));
assert_eq!(None, unsafe { bmp.pixels_at_ptr(-1, -1) });
let offset = bmp.get_offset_to_xy(1, 1);
let pixels = bmp.pixels_at_ptr(0, 0).unwrap();
let pixels = unsafe { bmp.pixels_at_ptr(0, 0).unwrap() };
assert_eq!(0, unsafe { *pixels });
assert_eq!(1, unsafe { *(pixels.add(offset)) });
assert_eq!(2, unsafe { *(pixels.add(63)) });
let pixels = bmp.pixels_at_ptr(1, 1).unwrap();
let pixels = unsafe { bmp.pixels_at_ptr(1, 1).unwrap() };
assert_eq!(1, unsafe { *pixels });
assert_eq!(2, unsafe { *(pixels.add(54)) });
}
@ -516,15 +500,15 @@ mod tests {
let mut bmp = Bitmap::<u8>::new(8, 8).unwrap();
bmp.pixels_mut().copy_from_slice(RAW_BMP_PIXELS);
assert_eq!(None, bmp.pixels_at_mut_ptr(-1, -1));
assert_eq!(None, unsafe { bmp.pixels_at_mut_ptr(-1, -1) });
let offset = bmp.get_offset_to_xy(1, 1);
let pixels = bmp.pixels_at_mut_ptr(0, 0).unwrap();
let pixels = unsafe { bmp.pixels_at_mut_ptr(0, 0).unwrap() };
assert_eq!(0, unsafe { *pixels });
assert_eq!(1, unsafe { *(pixels.add(offset)) });
assert_eq!(2, unsafe { *(pixels.add(63)) });
let pixels = bmp.pixels_at_mut_ptr(1, 1).unwrap();
let pixels = unsafe { bmp.pixels_at_mut_ptr(1, 1).unwrap() };
assert_eq!(1, unsafe { *pixels });
assert_eq!(2, unsafe { *(pixels.add(54)) });
}

View file

@ -5,7 +5,7 @@ use std::path::Path;
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use thiserror::Error;
use crate::graphics::{IndexedBitmap, Palette, PaletteError, PaletteFormat, RgbaBitmap};
use crate::graphics::{from_rgb32, IndexedBitmap, Palette, PaletteError, PaletteFormat, RgbaBitmap};
use crate::utils::ReadFixedLengthByteArray;
#[derive(Error, Debug)]
@ -178,7 +178,7 @@ impl IndexedBitmap {
Ok((bmp, palette))
}
pub fn load_pcx_file(path: impl AsRef<Path>) -> Result<(IndexedBitmap, Palette), PcxError> {
pub fn load_pcx_file(path: &Path) -> Result<(IndexedBitmap, Palette), PcxError> {
let f = File::open(path)?;
let mut reader = BufReader::new(f);
Self::load_pcx_bytes(&mut reader)
@ -241,16 +241,17 @@ impl IndexedBitmap {
writer.write_u8(0xc)?;
for i in 0..=255 {
let color = palette[i];
writer.write_u8(color.r())?;
writer.write_u8(color.g())?;
writer.write_u8(color.b())?;
let argb = palette[i];
let [r, g, b] = from_rgb32(argb);
writer.write_u8(r)?;
writer.write_u8(g)?;
writer.write_u8(b)?;
}
Ok(())
}
pub fn to_pcx_file(&self, path: impl AsRef<Path>, palette: &Palette) -> Result<(), PcxError> {
pub fn to_pcx_file(&self, path: &Path, palette: &Palette) -> Result<(), PcxError> {
let f = File::create(path)?;
let mut writer = BufWriter::new(f);
self.to_pcx_bytes(&mut writer, palette)
@ -267,7 +268,7 @@ impl RgbaBitmap {
Ok((output, palette))
}
pub fn load_pcx_file(path: impl AsRef<Path>) -> Result<(RgbaBitmap, Palette), PcxError> {
pub fn load_pcx_file(path: &Path) -> Result<(RgbaBitmap, Palette), PcxError> {
let (temp_bitmap, palette) = IndexedBitmap::load_pcx_file(path)?;
let output = temp_bitmap.to_rgba(&palette);
Ok((output, palette))
@ -286,7 +287,7 @@ mod tests {
const BASE_PATH: &str = "./test-assets/pcx/";
fn test_file(file: impl AsRef<Path>) -> PathBuf {
fn test_file(file: &Path) -> PathBuf {
PathBuf::from(BASE_PATH).join(file)
}
@ -294,14 +295,14 @@ mod tests {
pub fn load_and_save() -> Result<(), PcxError> {
let tmp_dir = TempDir::new()?;
let ref_pixels = load_raw_indexed(test_file("small.bin"))?;
let ref_pixels = load_raw_indexed(test_file(Path::new("small.bin")).as_path())?;
let dp2_palette = Palette::load_from_file(
test_assets_file("dp2.pal"), //
test_assets_file(Path::new("dp2.pal")).as_path(), //
PaletteFormat::Normal,
)
.unwrap();
let (bmp, palette) = IndexedBitmap::load_pcx_file(test_file("small.pcx"))?;
let (bmp, palette) = IndexedBitmap::load_pcx_file(test_file(Path::new("small.pcx")).as_path())?;
assert_eq!(16, bmp.width());
assert_eq!(16, bmp.height());
assert_eq!(bmp.pixels(), ref_pixels.as_ref());
@ -324,9 +325,9 @@ mod tests {
// first image
let ref_pixels = load_raw_indexed(test_file("large_1.bin"))?;
let ref_pixels = load_raw_indexed(test_file(Path::new("large_1.bin")).as_path())?;
let (bmp, palette) = IndexedBitmap::load_pcx_file(test_file("large_1.pcx"))?;
let (bmp, palette) = IndexedBitmap::load_pcx_file(test_file(Path::new("large_1.pcx")).as_path())?;
assert_eq!(320, bmp.width());
assert_eq!(200, bmp.height());
assert_eq!(bmp.pixels(), ref_pixels.as_ref());
@ -340,9 +341,9 @@ mod tests {
// second image
let ref_pixels = load_raw_indexed(test_file("large_2.bin"))?;
let ref_pixels = load_raw_indexed(test_file(Path::new("large_2.bin")).as_path())?;
let (bmp, palette) = IndexedBitmap::load_pcx_file(test_file("large_2.pcx"))?;
let (bmp, palette) = IndexedBitmap::load_pcx_file(test_file(Path::new("large_2.pcx")).as_path())?;
assert_eq!(320, bmp.width());
assert_eq!(200, bmp.height());
assert_eq!(bmp.pixels(), ref_pixels.as_ref());

View file

@ -7,7 +7,10 @@ use std::path::Path;
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use thiserror::Error;
use crate::graphics::{Bitmap, IndexedBitmap, Palette, PaletteError, PaletteFormat, Pixel, RgbaBitmap, RGBA};
use crate::graphics::{
from_argb32, from_rgb32, to_argb32, to_rgb32, Bitmap, IndexedBitmap, Palette, PaletteError, PaletteFormat, Pixel,
RgbaBitmap,
};
use crate::utils::ReadFixedLengthByteArray;
const PNG_HEADER: [u8; 8] = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];
@ -42,10 +45,10 @@ pub enum PngFormat {
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
enum ColorFormat {
Grayscale = 0,
Rgb = 2,
RGB = 2,
IndexedColor = 3,
GrayscaleAlpha = 4,
Rgba = 6,
RGBA = 6,
}
impl ColorFormat {
@ -53,10 +56,10 @@ impl ColorFormat {
use ColorFormat::*;
match value {
0 => Ok(Grayscale),
2 => Ok(Rgb),
2 => Ok(RGB),
3 => Ok(IndexedColor),
4 => Ok(GrayscaleAlpha),
6 => Ok(Rgba),
6 => Ok(RGBA),
_ => Err(PngError::UnsupportedColorType(value)),
}
}
@ -202,8 +205,8 @@ impl ScanlineBuffer {
pub fn new(ihdr: &ImageHeaderChunk) -> Result<Self, PngError> {
let bpp = match ihdr.format {
ColorFormat::IndexedColor => 1,
ColorFormat::Rgb => 3,
ColorFormat::Rgba => 4,
ColorFormat::RGB => 3,
ColorFormat::RGBA => 4,
_ => return Err(PngError::BadFile(format!("Unsupported color format: {:?}", ihdr.format))),
};
let stride = ihdr.width as usize * bpp;
@ -319,8 +322,8 @@ impl ScanlinePixelConverter<u8> for ScanlineBuffer {
}
}
impl ScanlinePixelConverter<RGBA> for ScanlineBuffer {
fn read_pixel(&mut self, x: usize, palette: &Option<Palette>) -> Result<RGBA, PngError> {
impl ScanlinePixelConverter<u32> for ScanlineBuffer {
fn read_pixel(&mut self, x: usize, palette: &Option<Palette>) -> Result<u32, PngError> {
let offset = x * self.bpp;
match self.format {
ColorFormat::IndexedColor => {
@ -333,37 +336,39 @@ impl ScanlinePixelConverter<RGBA> for ScanlineBuffer {
)))
}
}
ColorFormat::Rgb => {
ColorFormat::RGB => {
let r = self.current[offset];
let g = self.current[offset + 1];
let b = self.current[offset + 2];
Ok(RGBA::from_rgb([r, g, b]))
Ok(to_rgb32([r, g, b]))
}
ColorFormat::Rgba => {
ColorFormat::RGBA => {
let r = self.current[offset];
let g = self.current[offset + 1];
let b = self.current[offset + 2];
let a = self.current[offset + 3];
Ok(RGBA::from_rgba([r, g, b, a]))
Ok(to_argb32([a, r, g, b]))
}
_ => Err(PngError::BadFile(format!("Unsupported color format for this PixelReader: {:?}", self.format))),
}
}
fn write_pixel(&mut self, x: usize, pixel: RGBA) -> Result<(), PngError> {
fn write_pixel(&mut self, x: usize, pixel: u32) -> Result<(), PngError> {
let offset = x * self.bpp;
match self.format {
ColorFormat::Rgb => {
self.current[offset] = pixel.r();
self.current[offset + 1] = pixel.g();
self.current[offset + 2] = pixel.b();
ColorFormat::RGB => {
let [r, g, b] = from_rgb32(pixel);
self.current[offset] = r;
self.current[offset + 1] = g;
self.current[offset + 2] = b;
Ok(())
}
ColorFormat::Rgba => {
self.current[offset] = pixel.r();
self.current[offset + 1] = pixel.g();
self.current[offset + 2] = pixel.b();
self.current[offset + 3] = pixel.a();
ColorFormat::RGBA => {
let [a, r, g, b] = from_argb32(pixel);
self.current[offset] = r;
self.current[offset + 1] = g;
self.current[offset + 2] = b;
self.current[offset + 3] = a;
Ok(())
}
_ => Err(PngError::BadFile(format!("Unsupported color format for this PixelReader: {:?}", self.format))),
@ -400,8 +405,8 @@ where
return Err(PngError::BadFile(String::from("Unsupported color bit depth.")));
}
if ihdr.format != ColorFormat::IndexedColor // .
&& ihdr.format != ColorFormat::Rgb
&& ihdr.format != ColorFormat::Rgba
&& ihdr.format != ColorFormat::RGB
&& ihdr.format != ColorFormat::RGBA
{
return Err(PngError::BadFile(String::from("Unsupported pixel color format.")));
}
@ -560,7 +565,7 @@ impl IndexedBitmap {
load_png_bytes(reader)
}
pub fn load_png_file(path: impl AsRef<Path>) -> Result<(IndexedBitmap, Option<Palette>), PngError> {
pub fn load_png_file(path: &Path) -> Result<(IndexedBitmap, Option<Palette>), PngError> {
let f = File::open(path)?;
let mut reader = BufReader::new(f);
Self::load_png_bytes(&mut reader)
@ -570,7 +575,7 @@ impl IndexedBitmap {
write_png_bytes(writer, self, ColorFormat::IndexedColor, Some(palette))
}
pub fn to_png_file(&self, path: impl AsRef<Path>, palette: &Palette) -> Result<(), PngError> {
pub fn to_png_file(&self, path: &Path, palette: &Palette) -> Result<(), PngError> {
let f = File::create(path)?;
let mut writer = BufWriter::new(f);
self.to_png_bytes(&mut writer, palette)
@ -582,7 +587,7 @@ impl RgbaBitmap {
load_png_bytes(reader)
}
pub fn load_png_file(path: impl AsRef<Path>) -> Result<(RgbaBitmap, Option<Palette>), PngError> {
pub fn load_png_file(path: &Path) -> Result<(RgbaBitmap, Option<Palette>), PngError> {
let f = File::open(path)?;
let mut reader = BufReader::new(f);
Self::load_png_bytes(&mut reader)
@ -593,14 +598,14 @@ impl RgbaBitmap {
writer,
self,
match format {
PngFormat::RGB => ColorFormat::Rgb,
PngFormat::RGBA => ColorFormat::Rgba,
PngFormat::RGB => ColorFormat::RGB,
PngFormat::RGBA => ColorFormat::RGBA,
},
None,
)
}
pub fn to_png_file(&self, path: impl AsRef<Path>, format: PngFormat) -> Result<(), PngError> {
pub fn to_png_file(&self, path: &Path, format: PngFormat) -> Result<(), PngError> {
let f = File::create(path)?;
let mut writer = BufWriter::new(f);
self.to_png_bytes(&mut writer, format)
@ -614,20 +619,20 @@ mod tests {
use claim::*;
use tempfile::TempDir;
use crate::tests::{load_raw_indexed, load_raw_rgba};
use crate::tests::{load_raw_argb, load_raw_indexed};
use super::*;
const BASE_PATH: &str = "./test-assets/png/";
fn test_file(file: impl AsRef<Path>) -> PathBuf {
fn test_file(file: &Path) -> PathBuf {
PathBuf::from(BASE_PATH).join(file)
}
#[test]
pub fn loads_indexed_256_color() -> Result<(), PngError> {
let ref_bytes = load_raw_indexed(test_file("indexed_8.bin"))?;
let (bmp, palette) = IndexedBitmap::load_png_file(test_file("indexed_8.png"))?;
let ref_bytes = load_raw_indexed(test_file(Path::new("indexed_8.bin")).as_path())?;
let (bmp, palette) = IndexedBitmap::load_png_file(test_file(Path::new("indexed_8.png")).as_path())?;
assert!(palette.is_some());
assert_eq!(ref_bytes, bmp.pixels);
Ok(())
@ -635,8 +640,8 @@ mod tests {
#[test]
pub fn loads_indexed_256_color_to_rgba_destination() -> Result<(), PngError> {
let ref_bytes = load_raw_rgba(test_file("indexed_8_rgba.bin"))?;
let (bmp, palette) = RgbaBitmap::load_png_file(test_file("indexed_8.png"))?;
let ref_bytes = load_raw_argb(test_file(Path::new("indexed_8_rgba.bin")).as_path())?;
let (bmp, palette) = RgbaBitmap::load_png_file(test_file(Path::new("indexed_8.png")).as_path())?;
assert!(palette.is_some());
assert_eq!(ref_bytes, bmp.pixels);
Ok(())
@ -644,8 +649,8 @@ mod tests {
#[test]
pub fn loads_rgb_color() -> Result<(), PngError> {
let ref_bytes = load_raw_rgba(test_file("rgb.bin"))?;
let (bmp, palette) = RgbaBitmap::load_png_file(test_file("rgb.png"))?;
let ref_bytes = load_raw_argb(test_file(Path::new("rgb.bin")).as_path())?;
let (bmp, palette) = RgbaBitmap::load_png_file(test_file(Path::new("rgb.png")).as_path())?;
assert!(palette.is_none());
assert_eq!(ref_bytes, bmp.pixels);
Ok(())
@ -653,8 +658,8 @@ mod tests {
#[test]
pub fn loads_rgba_color() -> Result<(), PngError> {
let ref_bytes = load_raw_rgba(test_file("rgba.bin"))?;
let (bmp, palette) = RgbaBitmap::load_png_file(test_file("rgba.png"))?;
let ref_bytes = load_raw_argb(test_file(Path::new("rgba.bin")).as_path())?;
let (bmp, palette) = RgbaBitmap::load_png_file(test_file(Path::new("rgba.png")).as_path())?;
assert!(palette.is_none());
assert_eq!(ref_bytes, bmp.pixels);
Ok(())
@ -662,8 +667,8 @@ mod tests {
#[test]
pub fn loads_filter_0() -> Result<(), PngError> {
let ref_bytes = load_raw_rgba(test_file("filter_0_rgb.bin"))?;
let (bmp, palette) = RgbaBitmap::load_png_file(test_file("filter_0_rgb.png"))?;
let ref_bytes = load_raw_argb(test_file(Path::new("filter_0_rgb.bin")).as_path())?;
let (bmp, palette) = RgbaBitmap::load_png_file(test_file(Path::new("filter_0_rgb.png")).as_path())?;
assert!(palette.is_none());
assert_eq!(ref_bytes, bmp.pixels);
Ok(())
@ -671,8 +676,8 @@ mod tests {
#[test]
pub fn loads_filter_1() -> Result<(), PngError> {
let ref_bytes = load_raw_rgba(test_file("filter_1_rgb.bin"))?;
let (bmp, palette) = RgbaBitmap::load_png_file(test_file("filter_1_rgb.png"))?;
let ref_bytes = load_raw_argb(test_file(Path::new("filter_1_rgb.bin")).as_path())?;
let (bmp, palette) = RgbaBitmap::load_png_file(test_file(Path::new("filter_1_rgb.png")).as_path())?;
assert!(palette.is_none());
assert_eq!(ref_bytes, bmp.pixels);
Ok(())
@ -680,8 +685,8 @@ mod tests {
#[test]
pub fn loads_filter_2() -> Result<(), PngError> {
let ref_bytes = load_raw_rgba(test_file("filter_2_rgb.bin"))?;
let (bmp, palette) = RgbaBitmap::load_png_file(test_file("filter_2_rgb.png"))?;
let ref_bytes = load_raw_argb(test_file(Path::new("filter_2_rgb.bin")).as_path())?;
let (bmp, palette) = RgbaBitmap::load_png_file(test_file(Path::new("filter_2_rgb.png")).as_path())?;
assert!(palette.is_none());
assert_eq!(ref_bytes, bmp.pixels);
Ok(())
@ -689,8 +694,8 @@ mod tests {
#[test]
pub fn loads_filter_3() -> Result<(), PngError> {
let ref_bytes = load_raw_rgba(test_file("filter_3_rgb.bin"))?;
let (bmp, palette) = RgbaBitmap::load_png_file(test_file("filter_3_rgb.png"))?;
let ref_bytes = load_raw_argb(test_file(Path::new("filter_3_rgb.bin")).as_path())?;
let (bmp, palette) = RgbaBitmap::load_png_file(test_file(Path::new("filter_3_rgb.png")).as_path())?;
assert!(palette.is_none());
assert_eq!(ref_bytes, bmp.pixels);
Ok(())
@ -698,8 +703,8 @@ mod tests {
#[test]
pub fn loads_filter_4() -> Result<(), PngError> {
let ref_bytes = load_raw_rgba(test_file("filter_4_rgb.bin"))?;
let (bmp, palette) = RgbaBitmap::load_png_file(test_file("filter_4_rgb.png"))?;
let ref_bytes = load_raw_argb(test_file(Path::new("filter_4_rgb.bin")).as_path())?;
let (bmp, palette) = RgbaBitmap::load_png_file(test_file(Path::new("filter_4_rgb.png")).as_path())?;
assert!(palette.is_none());
assert_eq!(ref_bytes, bmp.pixels);
Ok(())
@ -707,13 +712,13 @@ mod tests {
#[test]
pub fn loads_larger_indexed_256color_images() -> Result<(), PngError> {
let ref_bytes = load_raw_indexed(test_file("large_1_indexed.bin"))?;
let (bmp, palette) = IndexedBitmap::load_png_file(test_file("large_1_indexed.png"))?;
let ref_bytes = load_raw_indexed(test_file(Path::new("large_1_indexed.bin")).as_path())?;
let (bmp, palette) = IndexedBitmap::load_png_file(test_file(Path::new("large_1_indexed.png")).as_path())?;
assert!(palette.is_some());
assert_eq!(ref_bytes, bmp.pixels);
let ref_bytes = load_raw_indexed(test_file("large_2_indexed.bin"))?;
let (bmp, palette) = IndexedBitmap::load_png_file(test_file("large_2_indexed.png"))?;
let ref_bytes = load_raw_indexed(test_file(Path::new("large_2_indexed.bin")).as_path())?;
let (bmp, palette) = IndexedBitmap::load_png_file(test_file(Path::new("large_2_indexed.png")).as_path())?;
assert!(palette.is_some());
assert_eq!(ref_bytes, bmp.pixels);
@ -722,13 +727,13 @@ mod tests {
#[test]
pub fn loads_larger_rgb_images() -> Result<(), PngError> {
let ref_bytes = load_raw_rgba(test_file("large_1_rgba.bin"))?;
let (bmp, palette) = RgbaBitmap::load_png_file(test_file("large_1_rgb.png"))?;
let ref_bytes = load_raw_argb(test_file(Path::new("large_1_rgba.bin")).as_path())?;
let (bmp, palette) = RgbaBitmap::load_png_file(test_file(Path::new("large_1_rgb.png")).as_path())?;
assert!(palette.is_none());
assert_eq!(ref_bytes, bmp.pixels);
let ref_bytes = load_raw_rgba(test_file("large_2_rgba.bin"))?;
let (bmp, palette) = RgbaBitmap::load_png_file(test_file("large_2_rgb.png"))?;
let ref_bytes = load_raw_argb(test_file(Path::new("large_2_rgba.bin")).as_path())?;
let (bmp, palette) = RgbaBitmap::load_png_file(test_file(Path::new("large_2_rgb.png")).as_path())?;
assert!(palette.is_none());
assert_eq!(ref_bytes, bmp.pixels);
@ -739,9 +744,9 @@ mod tests {
pub fn load_and_save_indexed_256_color() -> Result<(), PngError> {
let tmp_dir = TempDir::new()?;
let ref_bytes = load_raw_indexed(test_file("indexed_8.bin"))?;
let ref_bytes = load_raw_indexed(test_file(Path::new("indexed_8.bin")).as_path())?;
let (bmp, palette) = IndexedBitmap::load_png_file(test_file("indexed_8.png"))?;
let (bmp, palette) = IndexedBitmap::load_png_file(test_file(Path::new("indexed_8.png")).as_path())?;
assert_eq!(32, bmp.width());
assert_eq!(32, bmp.height());
assert_eq!(bmp.pixels, ref_bytes);
@ -764,9 +769,9 @@ mod tests {
// first image
let ref_bytes = load_raw_indexed(test_file("large_1_indexed.bin"))?;
let ref_bytes = load_raw_indexed(test_file(Path::new("large_1_indexed.bin")).as_path())?;
let (bmp, palette) = IndexedBitmap::load_png_file(test_file("large_1_indexed.png"))?;
let (bmp, palette) = IndexedBitmap::load_png_file(test_file(Path::new("large_1_indexed.png")).as_path())?;
assert_eq!(320, bmp.width());
assert_eq!(200, bmp.height());
assert_eq!(bmp.pixels, ref_bytes);
@ -782,9 +787,9 @@ mod tests {
// second image
let ref_bytes = load_raw_indexed(test_file("large_2_indexed.bin"))?;
let ref_bytes = load_raw_indexed(test_file(Path::new("large_2_indexed.bin")).as_path())?;
let (bmp, palette) = IndexedBitmap::load_png_file(test_file("large_2_indexed.png"))?;
let (bmp, palette) = IndexedBitmap::load_png_file(test_file(Path::new("large_2_indexed.png")).as_path())?;
assert_eq!(320, bmp.width());
assert_eq!(200, bmp.height());
assert_eq!(bmp.pixels, ref_bytes);
@ -805,9 +810,9 @@ mod tests {
pub fn load_and_save_rgb_color() -> Result<(), PngError> {
let tmp_dir = TempDir::new()?;
let ref_bytes = load_raw_rgba(test_file("rgb.bin"))?;
let ref_bytes = load_raw_argb(test_file(Path::new("rgb.bin")).as_path())?;
let (bmp, palette) = RgbaBitmap::load_png_file(test_file("rgb.png"))?;
let (bmp, palette) = RgbaBitmap::load_png_file(test_file(Path::new("rgb.png")).as_path())?;
assert_eq!(32, bmp.width());
assert_eq!(32, bmp.height());
assert_eq!(bmp.pixels, ref_bytes);
@ -830,9 +835,9 @@ mod tests {
// first image
let ref_bytes = load_raw_rgba(test_file("large_1_rgba.bin"))?;
let ref_bytes = load_raw_argb(test_file(Path::new("large_1_rgba.bin")).as_path())?;
let (bmp, palette) = RgbaBitmap::load_png_file(test_file("large_1_rgb.png"))?;
let (bmp, palette) = RgbaBitmap::load_png_file(test_file(Path::new("large_1_rgb.png")).as_path())?;
assert_eq!(320, bmp.width());
assert_eq!(200, bmp.height());
assert_eq!(bmp.pixels, ref_bytes);
@ -848,9 +853,9 @@ mod tests {
// second image
let ref_bytes = load_raw_rgba(test_file("large_2_rgba.bin"))?;
let ref_bytes = load_raw_argb(test_file(Path::new("large_2_rgba.bin")).as_path())?;
let (bmp, palette) = RgbaBitmap::load_png_file(test_file("large_2_rgb.png"))?;
let (bmp, palette) = RgbaBitmap::load_png_file(test_file(Path::new("large_2_rgb.png")).as_path())?;
assert_eq!(320, bmp.width());
assert_eq!(200, bmp.height());
assert_eq!(bmp.pixels, ref_bytes);
@ -869,43 +874,58 @@ mod tests {
#[test]
pub fn load_fails_on_unsupported_formats() -> Result<(), PngError> {
assert_matches!(RgbaBitmap::load_png_file(test_file("unsupported_alpha_8bit.png")), Err(PngError::BadFile(..)));
assert_matches!(
RgbaBitmap::load_png_file(test_file("unsupported_greyscale_8bit.png")),
RgbaBitmap::load_png_file(test_file(Path::new("unsupported_alpha_8bit.png")).as_path()),
Err(PngError::BadFile(..))
);
assert_matches!(
RgbaBitmap::load_png_file(test_file("unsupported_indexed_16col.png")),
RgbaBitmap::load_png_file(test_file(Path::new("unsupported_greyscale_8bit.png")).as_path()),
Err(PngError::BadFile(..))
);
assert_matches!(
RgbaBitmap::load_png_file(test_file(Path::new("unsupported_indexed_16col.png")).as_path()),
Err(PngError::BadFile(..))
);
assert_matches!(
RgbaBitmap::load_png_file(test_file(Path::new("unsupported_rgb_16bit.png")).as_path()),
Err(PngError::BadFile(..))
);
assert_matches!(
RgbaBitmap::load_png_file(test_file(Path::new("unsupported_rgba_16bit.png")).as_path()),
Err(PngError::BadFile(..))
);
assert_matches!(RgbaBitmap::load_png_file(test_file("unsupported_rgb_16bit.png")), Err(PngError::BadFile(..)));
assert_matches!(RgbaBitmap::load_png_file(test_file("unsupported_rgba_16bit.png")), Err(PngError::BadFile(..)));
assert_matches!(
IndexedBitmap::load_png_file(test_file("unsupported_alpha_8bit.png")),
IndexedBitmap::load_png_file(test_file(Path::new("unsupported_alpha_8bit.png")).as_path()),
Err(PngError::BadFile(..))
);
assert_matches!(
IndexedBitmap::load_png_file(test_file("unsupported_greyscale_8bit.png")),
IndexedBitmap::load_png_file(test_file(Path::new("unsupported_greyscale_8bit.png")).as_path()),
Err(PngError::BadFile(..))
);
assert_matches!(
IndexedBitmap::load_png_file(test_file("unsupported_indexed_16col.png")),
IndexedBitmap::load_png_file(test_file(Path::new("unsupported_indexed_16col.png")).as_path()),
Err(PngError::BadFile(..))
);
assert_matches!(
IndexedBitmap::load_png_file(test_file("unsupported_rgb_16bit.png")),
IndexedBitmap::load_png_file(test_file(Path::new("unsupported_rgb_16bit.png")).as_path()),
Err(PngError::BadFile(..))
);
assert_matches!(
IndexedBitmap::load_png_file(test_file("unsupported_rgba_16bit.png")),
IndexedBitmap::load_png_file(test_file(Path::new("unsupported_rgba_16bit.png")).as_path()),
Err(PngError::BadFile(..))
);
// also test the extra formats that IndexedBitmap does not support which RgbaBitmap does
// (anything not 256-color indexed basically ...)
assert_matches!(IndexedBitmap::load_png_file(test_file("rgb.png")), Err(PngError::BadFile(..)));
assert_matches!(IndexedBitmap::load_png_file(test_file("rgba.png")), Err(PngError::BadFile(..)));
assert_matches!(
IndexedBitmap::load_png_file(test_file(Path::new("rgb.png")).as_path()),
Err(PngError::BadFile(..))
);
assert_matches!(
IndexedBitmap::load_png_file(test_file(Path::new("rgba.png")).as_path()),
Err(PngError::BadFile(..))
);
Ok(())
}

View file

@ -31,12 +31,9 @@ impl<PixelType: Pixel> Bitmap<PixelType> {
}
}
/// Sets the pixel at the given coordinates to the color specified.
///
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the bitmap.
/// Sets the pixel at the given coordinates to the color specified. The coordinates are not
/// checked for validity, so it is up to you to ensure they lie within the bounds of the
/// bitmap.
#[inline]
pub unsafe fn set_pixel_unchecked(&mut self, x: i32, y: i32, color: PixelType) {
let p = self.pixels_at_mut_ptr_unchecked(x, y);
@ -45,12 +42,8 @@ impl<PixelType: Pixel> Bitmap<PixelType> {
/// Sets the pixel at the given coordinates to the color returned by the given function. The
/// given function is one that accepts a color value that corresponds to the current pixel at
/// the given coordinates.
///
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the bitmap.
/// the given coordinates. The coordinates are not checked for validity, so it is up to you to
/// ensure they lie within the bounds of the bitmap.
#[inline]
pub unsafe fn set_custom_pixel_unchecked(&mut self, x: i32, y: i32, pixel_fn: impl Fn(PixelType) -> PixelType) {
let p = self.pixels_at_mut_ptr_unchecked(x, y);
@ -64,12 +57,8 @@ impl<PixelType: Pixel> Bitmap<PixelType> {
self.pixels_at(x, y).map(|pixels| pixels[0])
}
/// Gets the pixel at the given coordinates.
///
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the bitmap.
/// Gets the pixel at the given coordinates. The coordinates are not checked for validity, so
/// it is up to you to ensure they lie within the bounds of the bitmap.
#[inline]
pub unsafe fn get_pixel_unchecked(&self, x: i32, y: i32) -> PixelType {
*(self.pixels_at_ptr_unchecked(x, y))

View file

@ -1,6 +1,6 @@
use crate::graphics::{
clip_blit, per_pixel_blit, per_pixel_flipped_blit, per_pixel_rotozoom_blit, BitmapAtlas, BlendFunction, RgbaBitmap,
RGBA,
clip_blit, per_pixel_blit, per_pixel_flipped_blit, per_pixel_rotozoom_blit, tint_argb32, BitmapAtlas,
BlendFunction, RgbaBitmap,
};
use crate::math::Rect;
@ -8,7 +8,7 @@ use crate::math::Rect;
pub enum RgbaBlitMethod {
/// Solid blit, no transparency or other per-pixel adjustments.
Solid,
SolidTinted(RGBA),
SolidTinted(u32),
SolidBlended(BlendFunction),
/// Same as [RgbaBlitMethod::Solid] but the drawn image can also be flipped horizontally
/// and/or vertically.
@ -19,7 +19,7 @@ pub enum RgbaBlitMethod {
SolidFlippedTinted {
horizontal_flip: bool,
vertical_flip: bool,
tint_color: RGBA,
tint_color: u32,
},
SolidFlippedBlended {
horizontal_flip: bool,
@ -27,30 +27,30 @@ pub enum RgbaBlitMethod {
blend: BlendFunction,
},
/// Transparent blit, the specified source color pixels are skipped.
Transparent(RGBA),
Transparent(u32),
TransparentTinted {
transparent_color: RGBA,
tint_color: RGBA,
transparent_color: u32,
tint_color: u32,
},
TransparentBlended {
transparent_color: RGBA,
transparent_color: u32,
blend: BlendFunction,
},
/// Same as [RgbaBlitMethod::Transparent] but the drawn image can also be flipped horizontally
/// and/or vertically.
TransparentFlipped {
transparent_color: RGBA,
transparent_color: u32,
horizontal_flip: bool,
vertical_flip: bool,
},
TransparentFlippedTinted {
transparent_color: RGBA,
transparent_color: u32,
horizontal_flip: bool,
vertical_flip: bool,
tint_color: RGBA,
tint_color: u32,
},
TransparentFlippedBlended {
transparent_color: RGBA,
transparent_color: u32,
horizontal_flip: bool,
vertical_flip: bool,
blend: BlendFunction,
@ -58,15 +58,15 @@ pub enum RgbaBlitMethod {
/// Same as [RgbaBlitMethod::Transparent] except that the visible pixels on the destination are all
/// drawn using the same color.
TransparentSingle {
transparent_color: RGBA,
draw_color: RGBA,
transparent_color: u32,
draw_color: u32,
},
/// Combination of [RgbaBlitMethod::TransparentFlipped] and [RgbaBlitMethod::TransparentSingle].
TransparentFlippedSingle {
transparent_color: RGBA,
transparent_color: u32,
horizontal_flip: bool,
vertical_flip: bool,
draw_color: RGBA,
draw_color: u32,
},
/// Rotozoom blit, works the same as [RgbaBlitMethod::Solid] except that rotation and scaling is
/// performed.
@ -79,7 +79,7 @@ pub enum RgbaBlitMethod {
angle: f32,
scale_x: f32,
scale_y: f32,
tint_color: RGBA,
tint_color: u32,
},
RotoZoomBlended {
angle: f32,
@ -92,36 +92,32 @@ pub enum RgbaBlitMethod {
angle: f32,
scale_x: f32,
scale_y: f32,
transparent_color: RGBA,
transparent_color: u32,
},
RotoZoomTransparentTinted {
angle: f32,
scale_x: f32,
scale_y: f32,
transparent_color: RGBA,
tint_color: RGBA,
transparent_color: u32,
tint_color: u32,
},
RotoZoomTransparentBlended {
angle: f32,
scale_x: f32,
scale_y: f32,
transparent_color: RGBA,
transparent_color: u32,
blend: BlendFunction,
},
}
impl RgbaBitmap {
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the source and destination bitmaps.
pub unsafe fn solid_tinted_blit(
&mut self,
src: &Self,
src_region: &Rect,
dest_x: i32,
dest_y: i32,
tint_color: RGBA,
tint_color: u32,
) {
per_pixel_blit(
self, //
@ -130,15 +126,11 @@ impl RgbaBitmap {
dest_x,
dest_y,
|src_pixels, dest_pixels| {
*dest_pixels = (*src_pixels).tint(tint_color);
*dest_pixels = tint_argb32(*src_pixels, tint_color);
},
);
}
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the source and destination bitmaps.
pub unsafe fn solid_blended_blit(
&mut self,
src: &Self,
@ -154,15 +146,11 @@ impl RgbaBitmap {
dest_x,
dest_y,
|src_pixels, dest_pixels| {
*dest_pixels = blend.blend(*src_pixels, *dest_pixels);
*dest_pixels = blend.blend_1u32(*src_pixels, *dest_pixels);
},
);
}
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the source and destination bitmaps.
pub unsafe fn solid_flipped_blended_blit(
&mut self,
src: &Self,
@ -182,15 +170,11 @@ impl RgbaBitmap {
horizontal_flip,
vertical_flip,
|src_pixels, dest_pixels| {
*dest_pixels = blend.blend(*src_pixels, *dest_pixels);
*dest_pixels = blend.blend_1u32(*src_pixels, *dest_pixels);
},
);
}
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the source and destination bitmaps.
pub unsafe fn solid_flipped_tinted_blit(
&mut self,
src: &Self,
@ -199,7 +183,7 @@ impl RgbaBitmap {
dest_y: i32,
horizontal_flip: bool,
vertical_flip: bool,
tint_color: RGBA,
tint_color: u32,
) {
per_pixel_flipped_blit(
self, //
@ -210,23 +194,19 @@ impl RgbaBitmap {
horizontal_flip,
vertical_flip,
|src_pixels, dest_pixels| {
*dest_pixels = (*src_pixels).tint(tint_color);
*dest_pixels = tint_argb32(*src_pixels, tint_color);
},
);
}
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the source and destination bitmaps.
pub unsafe fn transparent_tinted_blit(
&mut self,
src: &Self,
src_region: &Rect,
dest_x: i32,
dest_y: i32,
transparent_color: RGBA,
tint_color: RGBA,
transparent_color: u32,
tint_color: u32,
) {
per_pixel_blit(
self, //
@ -236,23 +216,19 @@ impl RgbaBitmap {
dest_y,
|src_pixels, dest_pixels| {
if *src_pixels != transparent_color {
*dest_pixels = (*src_pixels).tint(tint_color);
*dest_pixels = tint_argb32(*src_pixels, tint_color);
}
},
);
}
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the source and destination bitmaps.
pub unsafe fn transparent_blended_blit(
&mut self,
src: &Self,
src_region: &Rect,
dest_x: i32,
dest_y: i32,
transparent_color: RGBA,
transparent_color: u32,
blend: BlendFunction,
) {
per_pixel_blit(
@ -263,26 +239,22 @@ impl RgbaBitmap {
dest_y,
|src_pixels, dest_pixels| {
if *src_pixels != transparent_color {
*dest_pixels = blend.blend(*src_pixels, *dest_pixels);
*dest_pixels = blend.blend_1u32(*src_pixels, *dest_pixels);
}
},
);
}
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the source and destination bitmaps.
pub unsafe fn transparent_flipped_tinted_blit(
&mut self,
src: &Self,
src_region: &Rect,
dest_x: i32,
dest_y: i32,
transparent_color: RGBA,
transparent_color: u32,
horizontal_flip: bool,
vertical_flip: bool,
tint_color: RGBA,
tint_color: u32,
) {
per_pixel_flipped_blit(
self, //
@ -294,23 +266,19 @@ impl RgbaBitmap {
vertical_flip,
|src_pixels, dest_pixels| {
if *src_pixels != transparent_color {
*dest_pixels = (*src_pixels).tint(tint_color);
*dest_pixels = tint_argb32(*src_pixels, tint_color);
}
},
);
}
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the source and destination bitmaps.
pub unsafe fn transparent_flipped_blended_blit(
&mut self,
src: &Self,
src_region: &Rect,
dest_x: i32,
dest_y: i32,
transparent_color: RGBA,
transparent_color: u32,
horizontal_flip: bool,
vertical_flip: bool,
blend: BlendFunction,
@ -325,16 +293,12 @@ impl RgbaBitmap {
vertical_flip,
|src_pixels, dest_pixels| {
if *src_pixels != transparent_color {
*dest_pixels = blend.blend(*src_pixels, *dest_pixels);
*dest_pixels = blend.blend_1u32(*src_pixels, *dest_pixels);
}
},
);
}
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the source and destination bitmaps.
pub unsafe fn rotozoom_tinted_blit(
&mut self,
src: &Self,
@ -344,7 +308,7 @@ impl RgbaBitmap {
angle: f32,
scale_x: f32,
scale_y: f32,
tint_color: RGBA,
tint_color: u32,
) {
per_pixel_rotozoom_blit(
self, //
@ -356,15 +320,11 @@ impl RgbaBitmap {
scale_x,
scale_y,
|src_pixel, dest_bitmap, draw_x, draw_y| {
dest_bitmap.set_pixel(draw_x, draw_y, src_pixel.tint(tint_color));
dest_bitmap.set_pixel(draw_x, draw_y, tint_argb32(src_pixel, tint_color));
},
);
}
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the source and destination bitmaps.
pub unsafe fn rotozoom_blended_blit(
&mut self,
src: &Self,
@ -387,16 +347,12 @@ impl RgbaBitmap {
scale_y,
|src_pixel, dest_bitmap, 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_1u32(src_pixel, dest_pixel))
}
},
);
}
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the source and destination bitmaps.
pub unsafe fn rotozoom_transparent_tinted_blit(
&mut self,
src: &Self,
@ -406,8 +362,8 @@ impl RgbaBitmap {
angle: f32,
scale_x: f32,
scale_y: f32,
transparent_color: RGBA,
tint_color: RGBA,
transparent_color: u32,
tint_color: u32,
) {
per_pixel_rotozoom_blit(
self, //
@ -420,16 +376,12 @@ impl RgbaBitmap {
scale_y,
|src_pixel, dest_bitmap, draw_x, draw_y| {
if transparent_color != src_pixel {
dest_bitmap.set_pixel(draw_x, draw_y, src_pixel.tint(tint_color));
dest_bitmap.set_pixel(draw_x, draw_y, tint_argb32(src_pixel, tint_color));
}
},
);
}
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the source and destination bitmaps.
pub unsafe fn rotozoom_transparent_blended_blit(
&mut self,
src: &Self,
@ -439,7 +391,7 @@ impl RgbaBitmap {
angle: f32,
scale_x: f32,
scale_y: f32,
transparent_color: RGBA,
transparent_color: u32,
blend: BlendFunction,
) {
per_pixel_rotozoom_blit(
@ -454,7 +406,7 @@ impl RgbaBitmap {
|src_pixel, dest_bitmap, draw_x, draw_y| {
if transparent_color != src_pixel {
if let Some(dest_pixel) = dest_bitmap.get_pixel(draw_x, draw_y) {
dest_bitmap.set_pixel(draw_x, draw_y, blend.blend(src_pixel, dest_pixel))
dest_bitmap.set_pixel(draw_x, draw_y, blend.blend_1u32(src_pixel, dest_pixel))
}
}
},
@ -527,10 +479,6 @@ impl RgbaBitmap {
};
}
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the source and destination bitmaps.
#[inline]
#[rustfmt::skip]
pub unsafe fn blit_region_unchecked(
@ -613,20 +561,12 @@ impl RgbaBitmap {
}
}
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the source and destination bitmaps.
#[inline]
pub unsafe fn blit_unchecked(&mut self, method: RgbaBlitMethod, src: &Self, x: i32, y: i32) {
let src_region = Rect::new(0, 0, src.width, src.height);
self.blit_region_unchecked(method, src, &src_region, x, y);
}
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the source and destination bitmaps.
#[inline]
pub unsafe fn blit_atlas_unchecked(
&mut self,

View file

@ -1,7 +1,7 @@
use byteorder::ReadBytesExt;
use std::path::Path;
use crate::graphics::{Bitmap, BitmapError, Palette, RGBA};
use crate::graphics::{to_argb32, to_rgb32, Bitmap, BitmapError, Palette};
mod blit;
mod primitives;
@ -11,7 +11,7 @@ pub use blit::*;
pub use primitives::*;
pub use triangles::*;
pub type RgbaBitmap = Bitmap<RGBA>;
pub type RgbaBitmap = Bitmap<u32>;
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum RgbaPixelFormat {
@ -29,7 +29,7 @@ impl RgbaBitmap {
///
/// returns: `Result<Bitmap, BitmapError>`
pub fn new(width: u32, height: u32) -> Result<Self, BitmapError> {
Self::internal_new(width, height, RGBA::from_rgb([0, 0, 0]))
Self::internal_new(width, height, to_rgb32([0, 0, 0]))
}
pub fn from_bytes<T: ReadBytesExt>(
@ -38,7 +38,7 @@ impl RgbaBitmap {
format: RgbaPixelFormat,
reader: &mut T,
) -> Result<Self, BitmapError> {
let mut bitmap = Self::internal_new(width, height, RGBA::from_rgb([0, 0, 0]))?;
let mut bitmap = Self::internal_new(width, height, 0)?;
for pixel in bitmap.pixels_mut().iter_mut() {
*pixel = match format {
RgbaPixelFormat::RGBA => {
@ -46,22 +46,22 @@ impl RgbaBitmap {
let g = reader.read_u8()?;
let b = reader.read_u8()?;
let a = reader.read_u8()?;
RGBA::from_rgba([r, g, b, a])
to_argb32([a, r, g, b])
}
RgbaPixelFormat::ARGB => {
let a = reader.read_u8()?;
let r = reader.read_u8()?;
let g = reader.read_u8()?;
let b = reader.read_u8()?;
RGBA::from_rgba([r, g, b, a])
to_argb32([a, r, g, b])
}
};
}
Ok(bitmap)
}
pub fn load_file(path: impl AsRef<Path>) -> Result<(Self, Option<Palette>), BitmapError> {
if let Some(extension) = path.as_ref().extension() {
pub fn load_file(path: &Path) -> Result<(Self, Option<Palette>), BitmapError> {
if let Some(extension) = path.extension() {
let extension = extension.to_ascii_lowercase();
match extension.to_str() {
Some("png") => Ok(Self::load_png_file(path)?),

View file

@ -1,93 +1,85 @@
use crate::graphics::{BlendFunction, RgbaBitmap, RGBA};
use crate::graphics::{BlendFunction, RgbaBitmap};
impl RgbaBitmap {
/// Sets the pixel at the given coordinates using a blended color via the specified blend function
/// If the coordinates lie outside of the bitmaps clipping region, no pixels will be changed.
#[inline]
pub fn set_blended_pixel(&mut self, x: i32, y: i32, color: RGBA, blend: BlendFunction) {
pub fn set_blended_pixel(&mut self, x: i32, y: i32, color: u32, blend: BlendFunction) {
self.set_custom_pixel(
x, //
y,
|dest_color| blend.blend(color, dest_color),
|dest_color| blend.blend_1u32(color, dest_color),
);
}
/// Sets the pixel at the given coordinates using a blended color via the specified blend function.
///
/// # Safety
///
/// Coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// Sets the pixel at the given coordinates using a blended color via the specified blend function,
/// The coordinates are not checked for validity, so it is up to you to ensure they lie within the
/// bounds of the bitmap.
#[inline]
pub unsafe fn set_blended_pixel_unchecked(&mut self, x: i32, y: i32, color: RGBA, blend: BlendFunction) {
pub unsafe fn set_blended_pixel_unchecked(&mut self, x: i32, y: i32, color: u32, blend: BlendFunction) {
self.set_custom_pixel_unchecked(
x, //
y,
|dest_color| blend.blend(color, dest_color),
|dest_color| blend.blend_1u32(color, dest_color),
);
}
/// Draws a line from x1,y1 to x2,y2 by blending the drawn pixels using the given blend function.
#[inline]
pub fn blended_line(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: RGBA, blend: BlendFunction) {
pub fn blended_line(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: u32, blend: BlendFunction) {
self.line_custom(
x1, //
y1,
x2,
y2,
|dest_color| blend.blend(color, dest_color),
|dest_color| blend.blend_1u32(color, dest_color),
);
}
/// Draws a horizontal line from x1,y to x2,y by blending the drawn pixels using the given
/// blend function.
#[inline]
pub fn blended_horiz_line(&mut self, x1: i32, x2: i32, y: i32, color: RGBA, blend: BlendFunction) {
pub fn blended_horiz_line(&mut self, x1: i32, x2: i32, y: i32, color: u32, blend: BlendFunction) {
self.horiz_line_custom(
x1, //
x2,
y,
|dest_color| blend.blend(color, dest_color),
|dest_color| blend.blend_1u32(color, dest_color),
);
}
/// Draws a vertical line from x,y1 to x,y2 by blending the drawn pixels using the given blend
/// function.
#[inline]
pub fn blended_vert_line(&mut self, x: i32, y1: i32, y2: i32, color: RGBA, blend: BlendFunction) {
pub fn blended_vert_line(&mut self, x: i32, y1: i32, y2: i32, color: u32, blend: BlendFunction) {
self.vert_line_custom(
x, //
y1,
y2,
|dest_color| blend.blend(color, dest_color),
|dest_color| blend.blend_1u32(color, dest_color),
);
}
/// Draws an empty box (rectangle) using the points x1,y1 and x2,y2 to form the box to be
/// drawn, assuming they are specifying the top-left and bottom-right corners respectively.
/// The box is drawn by blending the drawn pixels using the given blend function.
#[inline]
pub fn blended_rect(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: RGBA, blend: BlendFunction) {
pub fn blended_rect(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: u32, blend: BlendFunction) {
self.rect_custom(
x1, //
y1,
x2,
y2,
|dest_color| blend.blend(color, dest_color),
|dest_color| blend.blend_1u32(color, dest_color),
);
}
/// Draws a filled box (rectangle) using the points x1,y1 and x2,y2 to form the box to be
/// drawn, assuming they are specifying the top-left and bottom-right corners respectively. The
/// filled box is draw by blending the drawn pixels using the given blend function.
#[inline]
pub fn blended_filled_rect(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: RGBA, 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(
x1, //
y1,
x2,
y2,
|dest_color| blend.blend(color, dest_color),
|dest_color| blend.blend_1u32(color, dest_color),
);
}
}

View file

@ -1,27 +1,29 @@
use std::simd;
use std::simd::prelude::{SimdFloat, SimdUint};
use crate::graphics::{edge_function, per_pixel_triangle_2d, BlendFunction, RgbaBitmap, RGBA};
use crate::graphics::{
edge_function, from_argb32_simd, from_rgb32_simd, multiply_argb_simd, per_pixel_triangle_2d, tint_argb_simd,
to_argb32_simd, to_rgb32_simd, BlendFunction, RgbaBitmap,
};
use crate::math::Vector2;
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum RgbaTriangle2d<'a> {
Solid {
position: [Vector2; 3], //
color: RGBA,
color: u32,
},
SolidBlended {
position: [Vector2; 3], //
color: RGBA,
color: u32,
blend: BlendFunction,
},
SolidMultiColor {
position: [Vector2; 3], //
color: [RGBA; 3],
color: [u32; 3],
},
SolidMultiColorBlended {
position: [Vector2; 3], //
color: [RGBA; 3],
color: [u32; 3],
blend: BlendFunction,
},
SolidTextured {
@ -32,26 +34,26 @@ pub enum RgbaTriangle2d<'a> {
SolidTexturedColored {
position: [Vector2; 3], //
texcoord: [Vector2; 3],
color: RGBA,
color: u32,
bitmap: &'a RgbaBitmap,
},
SolidTexturedColoredBlended {
position: [Vector2; 3], //
texcoord: [Vector2; 3],
color: RGBA,
color: u32,
bitmap: &'a RgbaBitmap,
blend: BlendFunction,
},
SolidTexturedMultiColored {
position: [Vector2; 3], //
texcoord: [Vector2; 3],
color: [RGBA; 3],
color: [u32; 3],
bitmap: &'a RgbaBitmap,
},
SolidTexturedMultiColoredBlended {
position: [Vector2; 3], //
texcoord: [Vector2; 3],
color: [RGBA; 3],
color: [u32; 3],
bitmap: &'a RgbaBitmap,
blend: BlendFunction,
},
@ -59,7 +61,7 @@ pub enum RgbaTriangle2d<'a> {
position: [Vector2; 3], //
texcoord: [Vector2; 3],
bitmap: &'a RgbaBitmap,
tint: RGBA,
tint: u32,
},
SolidTexturedBlended {
position: [Vector2; 3], //
@ -70,7 +72,7 @@ pub enum RgbaTriangle2d<'a> {
}
impl RgbaBitmap {
pub fn solid_triangle_2d(&mut self, positions: &[Vector2; 3], color: RGBA) {
pub fn solid_triangle_2d(&mut self, positions: &[Vector2; 3], color: u32) {
per_pixel_triangle_2d(
self, //
positions[0],
@ -80,21 +82,24 @@ impl RgbaBitmap {
)
}
pub fn solid_blended_triangle_2d(&mut self, positions: &[Vector2; 3], color: RGBA, blend: BlendFunction) {
pub fn solid_blended_triangle_2d(&mut self, positions: &[Vector2; 3], color: u32, blend: BlendFunction) {
let color = from_argb32_simd(color);
per_pixel_triangle_2d(
self, //
positions[0],
positions[1],
positions[2],
|dest_pixels, _w0, _w1, _w2| *dest_pixels = blend.blend(color, *dest_pixels),
|dest_pixels, _w0, _w1, _w2| {
*dest_pixels = to_argb32_simd(blend.blend_simd(color, from_argb32_simd(*dest_pixels)))
},
)
}
pub fn solid_multicolor_triangle_2d(&mut self, positions: &[Vector2; 3], colors: &[RGBA; 3]) {
pub fn solid_multicolor_triangle_2d(&mut self, positions: &[Vector2; 3], colors: &[u32; 3]) {
let area = simd::f32x4::splat(edge_function(positions[0], positions[1], positions[2]));
let color1 = colors[0].0.cast();
let color2 = colors[1].0.cast();
let color3 = colors[2].0.cast();
let color1 = from_rgb32_simd(colors[0]).cast();
let color2 = from_rgb32_simd(colors[1]).cast();
let color3 = from_rgb32_simd(colors[2]).cast();
per_pixel_triangle_2d(
self, //
positions[0],
@ -106,7 +111,7 @@ impl RgbaBitmap {
+ simd::f32x4::splat(w2) * color3)
/ area)
.cast();
*dest_pixels = RGBA(color)
*dest_pixels = to_rgb32_simd(color)
},
)
}
@ -114,13 +119,13 @@ impl RgbaBitmap {
pub fn solid_multicolor_blended_triangle_2d(
&mut self,
positions: &[Vector2; 3],
colors: &[RGBA; 3],
colors: &[u32; 3],
blend: BlendFunction,
) {
let area = simd::f32x4::splat(edge_function(positions[0], positions[1], positions[2]));
let color1 = colors[0].0.cast();
let color2 = colors[1].0.cast();
let color3 = colors[2].0.cast();
let color1 = from_argb32_simd(colors[0]).cast();
let color2 = from_argb32_simd(colors[1]).cast();
let color3 = from_argb32_simd(colors[2]).cast();
per_pixel_triangle_2d(
self, //
positions[0],
@ -132,7 +137,7 @@ impl RgbaBitmap {
+ simd::f32x4::splat(w2) * color3)
/ area)
.cast();
*dest_pixels = blend.blend(RGBA(color), *dest_pixels)
*dest_pixels = to_argb32_simd(blend.blend_simd(color, from_argb32_simd(*dest_pixels)))
},
)
}
@ -161,10 +166,11 @@ impl RgbaBitmap {
&mut self,
positions: &[Vector2; 3],
texcoords: &[Vector2; 3],
color: RGBA,
color: u32,
bitmap: &Self,
) {
let area = simd::f32x2::splat(edge_function(positions[0], positions[1], positions[2]));
let color = from_argb32_simd(color);
let texcoord1 = simd::f32x2::from_array([texcoords[0].x, texcoords[0].y]);
let texcoord2 = simd::f32x2::from_array([texcoords[1].x, texcoords[1].y]);
let texcoord3 = simd::f32x2::from_array([texcoords[2].x, texcoords[2].y]);
@ -178,8 +184,8 @@ impl RgbaBitmap {
+ simd::f32x2::splat(w1) * texcoord2
+ simd::f32x2::splat(w2) * texcoord3)
/ area;
let texel = bitmap.sample_at(texcoord[0], texcoord[1]);
*dest_pixels = texel * color
let texel = from_argb32_simd(bitmap.sample_at(texcoord[0], texcoord[1]));
*dest_pixels = to_argb32_simd(multiply_argb_simd(texel, color))
},
)
}
@ -188,11 +194,12 @@ impl RgbaBitmap {
&mut self,
positions: &[Vector2; 3],
texcoords: &[Vector2; 3],
color: RGBA,
color: u32,
bitmap: &Self,
blend: BlendFunction,
) {
let area = simd::f32x2::splat(edge_function(positions[0], positions[1], positions[2]));
let color = from_argb32_simd(color);
let texcoord1 = simd::f32x2::from_array([texcoords[0].x, texcoords[0].y]);
let texcoord2 = simd::f32x2::from_array([texcoords[1].x, texcoords[1].y]);
let texcoord3 = simd::f32x2::from_array([texcoords[2].x, texcoords[2].y]);
@ -206,9 +213,9 @@ impl RgbaBitmap {
+ simd::f32x2::splat(w1) * texcoord2
+ simd::f32x2::splat(w2) * texcoord3)
/ area;
let texel = bitmap.sample_at(texcoord[0], texcoord[1]);
let src = texel * color;
*dest_pixels = blend.blend(src, *dest_pixels)
let texel = from_argb32_simd(bitmap.sample_at(texcoord[0], texcoord[1]));
let src = multiply_argb_simd(texel, color);
*dest_pixels = to_argb32_simd(blend.blend_simd(src, from_argb32_simd(*dest_pixels)))
},
)
}
@ -217,13 +224,13 @@ impl RgbaBitmap {
&mut self,
positions: &[Vector2; 3],
texcoords: &[Vector2; 3],
colors: &[RGBA; 3],
colors: &[u32; 3],
bitmap: &Self,
) {
let area = simd::f32x4::splat(edge_function(positions[0], positions[1], positions[2]));
let color1 = colors[0].0.cast();
let color2 = colors[1].0.cast();
let color3 = colors[2].0.cast();
let color1 = from_rgb32_simd(colors[0]).cast();
let color2 = from_rgb32_simd(colors[1]).cast();
let color3 = from_rgb32_simd(colors[2]).cast();
// we are using a f32x4 here with two zero's at the end as dummy values just so that we can
// do the texture coordinate interpolation in the inner loop as f32x4 operations.
// however, for the texture coordinates, we only care about the first two lanes in the results ...
@ -241,8 +248,8 @@ impl RgbaBitmap {
let w2 = simd::f32x4::splat(w2);
let color = ((w0 * color1 + w1 * color2 + w2 * color3) / area).cast::<u8>();
let texcoord = (w0 * texcoord1 + w1 * texcoord2 + w2 * texcoord3) / area;
let texel = bitmap.sample_at(texcoord[0], texcoord[1]);
*dest_pixels = texel * RGBA(color)
let texel = from_argb32_simd(bitmap.sample_at(texcoord[0], texcoord[1]));
*dest_pixels = to_rgb32_simd(multiply_argb_simd(texel, color))
},
)
}
@ -251,14 +258,14 @@ impl RgbaBitmap {
&mut self,
positions: &[Vector2; 3],
texcoords: &[Vector2; 3],
colors: &[RGBA; 3],
colors: &[u32; 3],
bitmap: &Self,
blend: BlendFunction,
) {
let area = simd::f32x4::splat(edge_function(positions[0], positions[1], positions[2]));
let color1 = colors[0].0.cast();
let color2 = colors[1].0.cast();
let color3 = colors[2].0.cast();
let color1 = from_argb32_simd(colors[0]).cast();
let color2 = from_argb32_simd(colors[1]).cast();
let color3 = from_argb32_simd(colors[2]).cast();
// we are using a f32x4 here with two zero's at the end as dummy values just so that we can
// do the texture coordinate interpolation in the inner loop as f32x4 operations.
// however, for the texture coordinates, we only care about the first two lanes in the results ...
@ -276,9 +283,10 @@ impl RgbaBitmap {
let w2 = simd::f32x4::splat(w2);
let color = ((w0 * color1 + w1 * color2 + w2 * color3) / area).cast::<u8>();
let texcoord = (w0 * texcoord1 + w1 * texcoord2 + w2 * texcoord3) / area;
let texel = bitmap.sample_at(texcoord[0], texcoord[1]);
let src = texel * RGBA(color);
*dest_pixels = blend.blend(src, *dest_pixels)
let texel = from_argb32_simd(bitmap.sample_at(texcoord[0], texcoord[1]));
let src = multiply_argb_simd(texel, color);
let dest = from_argb32_simd(*dest_pixels);
*dest_pixels = to_argb32_simd(blend.blend_simd(src, dest))
},
)
}
@ -288,9 +296,10 @@ impl RgbaBitmap {
positions: &[Vector2; 3],
texcoords: &[Vector2; 3],
bitmap: &Self,
tint: RGBA,
tint: u32,
) {
let area = simd::f32x2::splat(edge_function(positions[0], positions[1], positions[2]));
let tint = from_argb32_simd(tint);
let texcoord1 = simd::f32x2::from_array([texcoords[0].x, texcoords[0].y]);
let texcoord2 = simd::f32x2::from_array([texcoords[1].x, texcoords[1].y]);
let texcoord3 = simd::f32x2::from_array([texcoords[2].x, texcoords[2].y]);
@ -304,8 +313,8 @@ impl RgbaBitmap {
+ simd::f32x2::splat(w1) * texcoord2
+ simd::f32x2::splat(w2) * texcoord3)
/ area;
let texel = bitmap.sample_at(texcoord[0], texcoord[1]);
*dest_pixels = texel.tint(tint);
let texel = from_argb32_simd(bitmap.sample_at(texcoord[0], texcoord[1]));
*dest_pixels = to_argb32_simd(tint_argb_simd(texel, tint));
},
)
}
@ -331,8 +340,8 @@ impl RgbaBitmap {
+ simd::f32x2::splat(w1) * texcoord2
+ simd::f32x2::splat(w2) * texcoord3)
/ area;
let texel = bitmap.sample_at(texcoord[0], texcoord[1]);
*dest_pixels = blend.blend(texel, *dest_pixels);
let texel = from_argb32_simd(bitmap.sample_at(texcoord[0], texcoord[1]));
*dest_pixels = to_argb32_simd(blend.blend_simd(texel, from_argb32_simd(*dest_pixels)));
},
)
}

View file

@ -1,26 +1,14 @@
use std::fs::File;
use std::io::{BufReader, BufWriter, Read, Write};
use std::ops::Index;
use std::path::Path;
use thiserror::Error;
use crate::graphics::{GeneralBitmap, GeneralBlitMethod};
use crate::graphics::GeneralBitmap;
use crate::math::Rect;
#[derive(Error, Debug)]
pub enum BitmapAtlasError {
#[error("Region is out of bounds for the Bitmap used by the BitmapAtlas")]
OutOfBounds,
#[error("Tile index {0} is invalid / out of range")]
InvalidTileIndex(usize),
#[error("Invalid dimensions for region")]
InvalidDimensions,
#[error("No bitmap atlas entries in the descriptor")]
NoEntries,
}
#[derive(Debug, Clone, Eq, PartialEq)]
@ -46,31 +34,7 @@ where
}
}
pub fn from_descriptor(descriptor: &BitmapAtlasDescriptor, bitmap: BitmapType) -> Result<Self, BitmapAtlasError> {
if descriptor.tiles.is_empty() {
return Err(BitmapAtlasError::NoEntries);
}
let mut atlas = BitmapAtlas::new(bitmap);
for entry in descriptor.tiles.iter() {
use BitmapAtlasDescriptorEntry::*;
match entry {
Tile { x, y, width, height } => {
atlas.add(Rect::new(*x as i32, *y as i32, *width, *height))?;
}
Autogrid { x, y, tile_width, tile_height, num_tiles_x, num_tiles_y, border } => {
atlas.add_custom_grid(*x, *y, *tile_width, *tile_height, *num_tiles_x, *num_tiles_y, *border)?;
}
}
}
Ok(atlas)
}
pub fn add(&mut self, rect: Rect) -> Result<usize, BitmapAtlasError> {
if rect.width == 0 || rect.height == 0 {
return Err(BitmapAtlasError::InvalidDimensions);
}
if !self.bounds.contains_rect(&rect) {
return Err(BitmapAtlasError::OutOfBounds);
}
@ -80,9 +44,6 @@ where
}
pub fn add_grid(&mut self, tile_width: u32, tile_height: u32) -> Result<usize, BitmapAtlasError> {
if tile_width == 0 || tile_height == 0 {
return Err(BitmapAtlasError::InvalidDimensions);
}
if self.bounds.width < tile_width || self.bounds.height < tile_height {
return Err(BitmapAtlasError::OutOfBounds);
}
@ -109,10 +70,6 @@ where
y_tiles: u32,
border: u32,
) -> Result<usize, BitmapAtlasError> {
if tile_width == 0 || tile_height == 0 {
return Err(BitmapAtlasError::InvalidDimensions);
}
// figure out of the grid properties given would result in us creating any
// rects that lie out of the bounds of this bitmap
let grid_region = Rect::new(
@ -157,31 +114,10 @@ where
self.tiles.get(index)
}
pub fn get_uv(&self, index: usize) -> Option<[f32; 4]> {
self.tiles.get(index).map(|rect| {
[
(rect.x as f32 / self.bitmap.width() as f32),
(rect.y as f32 / self.bitmap.height() as f32),
((rect.x + rect.width as i32) as f32 / self.bitmap.width() as f32),
((rect.y + rect.height as i32) as f32 / self.bitmap.height() as f32),
]
})
}
#[inline]
pub fn bitmap(&self) -> &BitmapType {
&self.bitmap
}
pub fn clone_tile(&self, index: usize) -> Result<BitmapType, BitmapAtlasError> {
if let Some(tile_rect) = self.get(index) {
let mut tile_bitmap = BitmapType::new(tile_rect.width, tile_rect.height).unwrap();
tile_bitmap.blit_region(GeneralBlitMethod::Solid, &self.bitmap, tile_rect, 0, 0);
Ok(tile_bitmap)
} else {
Err(BitmapAtlasError::InvalidTileIndex(index))
}
}
}
impl<BitmapType> Index<usize> for BitmapAtlas<BitmapType>
@ -196,70 +132,6 @@ where
}
}
#[derive(Error, Debug)]
pub enum BitmapAtlasDescriptorError {
#[error("Serde Json serialization/deserialization error: {0}")]
SerdeJsonError(String),
#[error("I/O error")]
IOError(#[from] std::io::Error),
}
#[derive(Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "lowercase")]
pub enum BitmapAtlasDescriptorEntry {
Tile {
x: u32, //
y: u32,
width: u32,
height: u32,
},
Autogrid {
x: u32, //
y: u32,
tile_width: u32,
tile_height: u32,
num_tiles_x: u32,
num_tiles_y: u32,
border: u32,
},
}
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct BitmapAtlasDescriptor {
pub bitmap: String,
pub tiles: Vec<BitmapAtlasDescriptorEntry>,
}
impl BitmapAtlasDescriptor {
pub fn load_from_file(path: impl AsRef<Path>) -> Result<Self, BitmapAtlasDescriptorError> {
let f = File::open(path)?;
let mut reader = BufReader::new(f);
Self::load_from_bytes(&mut reader)
}
pub fn load_from_bytes<T: Read>(reader: &mut T) -> Result<Self, BitmapAtlasDescriptorError> {
match serde_json::from_reader(reader) {
Ok(desc) => Ok(desc),
Err(error) => Err(BitmapAtlasDescriptorError::SerdeJsonError(error.to_string())),
}
}
pub fn to_file(&self, path: impl AsRef<Path>) -> Result<(), BitmapAtlasDescriptorError> {
let f = File::create(path)?;
let mut writer = BufWriter::new(f);
self.to_bytes(&mut writer)
}
pub fn to_bytes<T: Write>(&self, writer: &mut T) -> Result<(), BitmapAtlasDescriptorError> {
if let Err(error) = serde_json::to_writer_pretty(writer, &self) {
Err(BitmapAtlasDescriptorError::SerdeJsonError(error.to_string()))
} else {
Ok(())
}
}
}
#[cfg(test)]
mod tests {
use claim::*;
@ -298,16 +170,6 @@ mod tests {
let bmp = IndexedBitmap::new(64, 64).unwrap();
let mut atlas = BitmapAtlas::new(bmp);
assert_eq!(3, atlas.add_grid(32, 32).unwrap());
assert_eq!(4, atlas.len());
assert_eq!(Rect::new(0, 0, 32, 32), atlas[0]);
assert_eq!(Rect::new(32, 0, 32, 32), atlas[1]);
assert_eq!(Rect::new(0, 32, 32, 32), atlas[2]);
assert_eq!(Rect::new(32, 32, 32, 32), atlas[3]);
atlas.clear();
assert_eq!(0, atlas.len());
assert_eq!(3, atlas.add_custom_grid(0, 0, 8, 8, 2, 2, 0).unwrap());
assert_eq!(4, atlas.len());
assert_eq!(Rect::new(0, 0, 8, 8), atlas[0]);
@ -325,26 +187,4 @@ mod tests {
assert_eq!(Rect::new(0, 9, 4, 8), atlas[2]);
assert_eq!(Rect::new(5, 9, 4, 8), atlas[3]);
}
#[test]
pub fn adding_with_invalid_dimensions_fails() {
let bmp = IndexedBitmap::new(64, 64).unwrap();
let mut atlas = BitmapAtlas::new(bmp);
assert_matches!(atlas.add(Rect::new(0, 0, 0, 0)), Err(BitmapAtlasError::InvalidDimensions));
assert_matches!(atlas.add(Rect::new(16, 16, 0, 0)), Err(BitmapAtlasError::InvalidDimensions));
assert_matches!(atlas.add(Rect::new(16, 16, 8, 0)), Err(BitmapAtlasError::InvalidDimensions));
assert_matches!(atlas.add(Rect::new(16, 16, 0, 8)), Err(BitmapAtlasError::InvalidDimensions));
assert_eq!(0, atlas.len());
assert_matches!(atlas.add_grid(0, 0), Err(BitmapAtlasError::InvalidDimensions));
assert_matches!(atlas.add_grid(8, 0), Err(BitmapAtlasError::InvalidDimensions));
assert_matches!(atlas.add_grid(0, 8), Err(BitmapAtlasError::InvalidDimensions));
assert_eq!(0, atlas.len());
assert_matches!(atlas.add_custom_grid(0, 0, 0, 0, 2, 2, 1), Err(BitmapAtlasError::InvalidDimensions));
assert_matches!(atlas.add_custom_grid(0, 0, 8, 0, 2, 2, 1), Err(BitmapAtlasError::InvalidDimensions));
assert_matches!(atlas.add_custom_grid(0, 0, 0, 8, 2, 2, 1), Err(BitmapAtlasError::InvalidDimensions));
assert_eq!(0, atlas.len());
}
}

View file

@ -5,7 +5,7 @@ use std::path::Path;
use byteorder::{ReadBytesExt, WriteBytesExt};
use thiserror::Error;
use crate::graphics::Palette;
use crate::graphics::{from_rgb32, luminance, Palette};
use crate::math::lerp;
use crate::utils::ReadFixedLengthByteArray;
@ -76,7 +76,8 @@ impl BlendMap {
let mut blend_map = Self::new(source_color, source_color);
for idx in 0..=255 {
let lit = (palette[idx].luminance() * 255.0) as u8;
let rgb = from_rgb32(palette[idx]);
let lit = (luminance(rgb) * 255.0) as u8;
blend_map
.set_mapping(
source_color,
@ -104,9 +105,11 @@ impl BlendMap {
let mut blend_map = BlendMap::new(0, 255);
for source_color in 0..=255 {
let source_luminance = palette[source_color].luminance();
let source_rgb = from_rgb32(palette[source_color]);
let source_luminance = luminance(source_rgb);
for dest_color in 0..=255 {
let destination_luminance = palette[dest_color].luminance();
let dest_rgb = from_rgb32(palette[dest_color]);
let destination_luminance = luminance(dest_rgb);
let weight = (f(source_luminance, destination_luminance) * 255.0) as u8;
blend_map
.set_mapping(
@ -132,10 +135,10 @@ impl BlendMap {
pub fn new_translucency_map(blend_r: f32, blend_g: f32, blend_b: f32, palette: &Palette) -> Self {
let mut blend_map = BlendMap::new(0, 255);
for source in 0..=255 {
let [_, source_r, source_g, source_b] = palette[source].to_array();
let [source_r, source_g, source_b] = from_rgb32(palette[source]);
let mapping = blend_map.get_mapping_mut(source).unwrap();
for dest in 0..=255 {
let [_, dest_r, dest_g, dest_b] = palette[dest].to_array();
let [dest_r, dest_g, dest_b] = from_rgb32(palette[dest]);
let find_r = lerp(dest_r as f32, source_r as f32, blend_r) as u8;
let find_g = lerp(dest_g as f32, source_g as f32, blend_g) as u8;
@ -239,7 +242,7 @@ impl BlendMap {
self.get_mapping(source_color).map(|mapping| mapping[dest_color as usize])
}
pub fn load_from_file(path: impl AsRef<Path>) -> Result<Self, BlendMapError> {
pub fn load_from_file(path: &Path) -> Result<Self, BlendMapError> {
let f = File::open(path)?;
let mut reader = BufReader::new(f);
Self::load_from_bytes(&mut reader)
@ -268,7 +271,7 @@ impl BlendMap {
})
}
pub fn to_file(&self, path: impl AsRef<Path>) -> Result<(), BlendMapError> {
pub fn to_file(&self, path: &Path) -> Result<(), BlendMapError> {
let f = File::create(path)?;
let mut writer = BufWriter::new(f);
self.to_bytes(&mut writer)

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,5 @@
use std::fs::File;
use std::io::{BufReader, BufWriter, Cursor};
use std::ops::{Index, IndexMut};
use std::path::Path;
use byteorder::{ReadBytesExt, WriteBytesExt};
@ -22,15 +21,6 @@ pub enum FontError {
#[error("Font I/O error")]
IOError(#[from] std::io::Error),
#[error("Invalid character dimensions")]
InvalidCharacterDimensions,
#[error("Invalid number of characters")]
InvalidNumberOfCharacters,
#[error("Invalid line height")]
InvalidLineHeight,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
@ -57,38 +47,12 @@ pub trait Font {
PixelType: Pixel;
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct BitmaskCharacter {
bytes: [u8; CHAR_HEIGHT],
bounds: Rect,
}
impl Default for BitmaskCharacter {
fn default() -> Self {
BitmaskCharacter {
//
bytes: [0u8; CHAR_HEIGHT],
bounds: Rect::new(0, 0, CHAR_FIXED_WIDTH as u32, CHAR_HEIGHT as u32),
}
}
}
impl Index<u8> for BitmaskCharacter {
type Output = u8;
#[inline]
fn index(&self, index: u8) -> &Self::Output {
&self.bytes[index as usize]
}
}
impl IndexMut<u8> for BitmaskCharacter {
#[inline]
fn index_mut(&mut self, index: u8) -> &mut Self::Output {
&mut self.bytes[index as usize]
}
}
impl Character for BitmaskCharacter {
#[inline]
fn bounds(&self) -> &Rect {
@ -130,42 +94,6 @@ impl Character for BitmaskCharacter {
}
}
impl BitmaskCharacter {
pub fn new(bytes: [u8; CHAR_HEIGHT], width: usize) -> Result<BitmaskCharacter, FontError> {
if width < 1 || width > CHAR_FIXED_WIDTH {
return Err(FontError::InvalidCharacterDimensions);
}
Ok(BitmaskCharacter { bytes, bounds: Rect::new(0, 0, width as u32, CHAR_HEIGHT as u32) })
}
#[inline]
pub fn width(&self) -> u8 {
self.bounds.width as u8
}
pub fn set_width(&mut self, width: u8) -> Result<(), FontError> {
if width < 1 || width > CHAR_FIXED_WIDTH as u8 {
return Err(FontError::InvalidCharacterDimensions);
}
self.bounds.width = width as u32;
Ok(())
}
#[inline]
pub fn height(&self) -> u8 {
self.bounds.height as u8
}
pub fn set_height(&mut self, height: u8) -> Result<(), FontError> {
if height < 1 || height > CHAR_HEIGHT as u8 {
return Err(FontError::InvalidCharacterDimensions);
}
self.bounds.height = height as u32;
Ok(())
}
}
#[derive(Clone, Eq, PartialEq)]
pub struct BitmaskFont {
characters: Box<[BitmaskCharacter]>,
@ -188,28 +116,7 @@ impl BitmaskFont {
BitmaskFont::load_from_bytes(&mut Cursor::new(VGA_FONT_BYTES))
}
pub fn new(characters: &[BitmaskCharacter], line_height: usize) -> Result<BitmaskFont, FontError> {
if characters.len() != NUM_CHARS {
return Err(FontError::InvalidNumberOfCharacters);
}
if line_height < 1 || line_height > CHAR_HEIGHT {
return Err(FontError::InvalidLineHeight);
}
let mut font = BitmaskFont {
characters: Box::from(characters),
line_height: line_height as u8,
space_width: characters[' ' as usize].bounds.width as u8,
};
for i in 0..NUM_CHARS {
font.characters[i].bounds.height = line_height as u32;
}
Ok(font)
}
pub fn load_from_file(path: impl AsRef<Path>) -> Result<BitmaskFont, FontError> {
pub fn load_from_file(path: &Path) -> Result<BitmaskFont, FontError> {
let f = File::open(path)?;
let mut reader = BufReader::new(f);
@ -232,17 +139,26 @@ impl BitmaskFont {
}
// read character widths (used for rendering)
for character in characters.iter_mut().take(NUM_CHARS) {
character.bounds.width = reader.read_u8()? as u32;
for i in 0..NUM_CHARS {
characters[i].bounds.width = reader.read_u8()? as u32;
}
// read global font height (used for rendering)
let line_height = reader.read_u8()?;
Self::new(&characters, line_height as usize)
for i in 0..NUM_CHARS {
characters[i].bounds.height = line_height as u32;
}
pub fn to_file(&self, path: impl AsRef<Path>) -> Result<(), FontError> {
let space_width = characters[' ' as usize].bounds.width as u8;
Ok(BitmaskFont {
characters: characters.into_boxed_slice(), //
line_height,
space_width,
})
}
pub fn to_file(&self, path: &Path) -> Result<(), FontError> {
let f = File::create(path)?;
let mut writer = BufWriter::new(f);
self.to_bytes(&mut writer)
@ -327,13 +243,13 @@ mod tests {
const BASE_PATH: &str = "./test-assets/font/";
fn test_file(file: impl AsRef<Path>) -> PathBuf {
fn test_file(file: &Path) -> PathBuf {
PathBuf::from(BASE_PATH).join(file)
}
#[test]
pub fn load_font() -> Result<(), FontError> {
let font = BitmaskFont::load_from_file(test_file("vga.fnt"))?;
let font = BitmaskFont::load_from_file(test_file(Path::new("vga.fnt")).as_path())?;
assert_eq!(256, font.characters.len());
assert_eq!(CHAR_FIXED_WIDTH as u8, font.space_width);
for character in font.characters.iter() {
@ -347,7 +263,7 @@ mod tests {
#[test]
pub fn measure_text() -> Result<(), FontError> {
{
let font = BitmaskFont::load_from_file(test_file("vga.fnt"))?;
let font = BitmaskFont::load_from_file(test_file(Path::new("vga.fnt")).as_path())?;
assert_eq!((40, 8), font.measure("Hello", FontRenderOpts::<u8>::None));
assert_eq!((40, 16), font.measure("Hello\nthere", FontRenderOpts::<u8>::None));
@ -361,7 +277,7 @@ mod tests {
}
{
let font = BitmaskFont::load_from_file(test_file("small.fnt"))?;
let font = BitmaskFont::load_from_file(test_file(Path::new("small.fnt")).as_path())?;
assert_eq!((22, 7), font.measure("Hello", FontRenderOpts::<u8>::None));
assert_eq!((24, 14), font.measure("Hello\nthere", FontRenderOpts::<u8>::None));

View file

@ -1,3 +1,4 @@
use num_traits::{PrimInt, Unsigned};
use std::fmt::Display;
mod bitmap;
@ -15,5 +16,5 @@ pub use font::*;
pub use palette::*;
/// Common trait to represent single pixel/colour values.
pub trait Pixel: Default + Display + Eq + Copy + Clone {}
impl<T> Pixel for T where T: Default + Display + Eq + Copy + Clone {}
pub trait Pixel: PrimInt + Unsigned + Default + Display {}
impl<T> Pixel for T where T: PrimInt + Unsigned + Default + Display {}

View file

@ -7,7 +7,7 @@ use std::path::Path;
use byteorder::{ReadBytesExt, WriteBytesExt};
use thiserror::Error;
use crate::graphics::{IndexedBitmap, RGBA};
use crate::graphics::{from_rgb32, lerp_rgb32, to_argb32, to_rgb32, IndexedBitmap};
use crate::utils::abs_diff;
const NUM_COLORS: usize = 256;
@ -29,63 +29,67 @@ fn to_6bit(value: u8) -> u8 {
}
// vga bios (0-63) format
fn read_palette_6bit<T: ReadBytesExt>(reader: &mut T, num_colors: usize) -> Result<[RGBA; NUM_COLORS], PaletteError> {
fn read_palette_6bit<T: ReadBytesExt>(reader: &mut T, num_colors: usize) -> Result<[u32; NUM_COLORS], PaletteError> {
if num_colors > NUM_COLORS {
return Err(PaletteError::OutOfRange(num_colors));
}
let mut colors = [RGBA::from_rgba([0, 0, 0, 255]); NUM_COLORS];
for color in colors.iter_mut().take(num_colors) {
let mut colors = [to_argb32([255, 0, 0, 0]); NUM_COLORS];
for i in 0..num_colors {
let r = reader.read_u8()?;
let g = reader.read_u8()?;
let b = reader.read_u8()?;
*color = RGBA::from_rgb([from_6bit(r), from_6bit(g), from_6bit(b)]);
let color = to_rgb32([from_6bit(r), from_6bit(g), from_6bit(b)]);
colors[i] = color;
}
Ok(colors)
}
fn write_palette_6bit<T: WriteBytesExt>(
writer: &mut T,
colors: &[RGBA; NUM_COLORS],
colors: &[u32; NUM_COLORS],
num_colors: usize,
) -> Result<(), PaletteError> {
if num_colors > NUM_COLORS {
return Err(PaletteError::OutOfRange(num_colors));
}
for color in colors.iter().take(num_colors) {
writer.write_u8(to_6bit(color.r()))?;
writer.write_u8(to_6bit(color.g()))?;
writer.write_u8(to_6bit(color.b()))?;
for i in 0..num_colors {
let [r, g, b] = from_rgb32(colors[i]);
writer.write_u8(to_6bit(r))?;
writer.write_u8(to_6bit(g))?;
writer.write_u8(to_6bit(b))?;
}
Ok(())
}
// normal (0-255) format
fn read_palette_8bit<T: ReadBytesExt>(reader: &mut T, num_colors: usize) -> Result<[RGBA; NUM_COLORS], PaletteError> {
fn read_palette_8bit<T: ReadBytesExt>(reader: &mut T, num_colors: usize) -> Result<[u32; NUM_COLORS], PaletteError> {
if num_colors > NUM_COLORS {
return Err(PaletteError::OutOfRange(num_colors));
}
let mut colors = [RGBA::from_rgba([0, 0, 0, 255]); NUM_COLORS];
for color in colors.iter_mut().take(num_colors) {
let mut colors = [to_argb32([255, 0, 0, 0]); NUM_COLORS];
for i in 0..num_colors {
let r = reader.read_u8()?;
let g = reader.read_u8()?;
let b = reader.read_u8()?;
*color = RGBA::from_rgb([r, g, b]);
let color = to_rgb32([r, g, b]);
colors[i] = color;
}
Ok(colors)
}
fn write_palette_8bit<T: WriteBytesExt>(
writer: &mut T,
colors: &[RGBA; NUM_COLORS],
colors: &[u32; NUM_COLORS],
num_colors: usize,
) -> Result<(), PaletteError> {
if num_colors > NUM_COLORS {
return Err(PaletteError::OutOfRange(num_colors));
}
for color in colors.iter().take(num_colors) {
writer.write_u8(color.r())?;
writer.write_u8(color.g())?;
writer.write_u8(color.b())?;
for i in 0..num_colors {
let [r, g, b] = from_rgb32(colors[i]);
writer.write_u8(r)?;
writer.write_u8(g)?;
writer.write_u8(b)?;
}
Ok(())
}
@ -110,18 +114,19 @@ pub enum PaletteFormat {
/// colors are all stored individually as 32-bit packed values in the format 0xAARRGGBB.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Palette {
colors: [RGBA; NUM_COLORS],
colors: [u32; NUM_COLORS],
}
impl Palette {
/// Creates a new Palette with all black colors.
pub fn new() -> Palette {
Palette { colors: [RGBA::from_rgb([0, 0, 0]); NUM_COLORS] }
Palette { colors: [0; NUM_COLORS] }
}
/// Creates a new Palette with all initial colors having the RGB values specified.
pub fn new_with_default(r: u8, g: u8, b: u8) -> Palette {
Palette { colors: [RGBA::from_rgb([r, g, b]); NUM_COLORS] }
let rgb = to_rgb32([r, g, b]);
Palette { colors: [rgb; NUM_COLORS] }
}
/// Creates a new Palette, pre-loaded with the default VGA BIOS colors.
@ -135,7 +140,7 @@ impl Palette {
///
/// * `path`: the path of the palette file to be loaded
/// * `format`: the format that the palette data is expected to be in
pub fn load_from_file(path: impl AsRef<Path>, format: PaletteFormat) -> Result<Palette, PaletteError> {
pub fn load_from_file(path: &Path, format: PaletteFormat) -> Result<Palette, PaletteError> {
let f = File::open(path)?;
let mut reader = BufReader::new(f);
Self::load_from_bytes(&mut reader, format)
@ -166,7 +171,7 @@ impl Palette {
/// * `format`: the format that the palette data is expected to be in
/// * `num_colors`: the expected number of colors in the palette to be loaded (<= 256)
pub fn load_num_colors_from_file(
path: impl AsRef<Path>,
path: &Path,
format: PaletteFormat,
num_colors: usize,
) -> Result<Palette, PaletteError> {
@ -206,7 +211,7 @@ impl Palette {
///
/// * `path`: the path of the file to save the palette to
/// * `format`: the format to write the palette data in
pub fn to_file(&self, path: impl AsRef<Path>, format: PaletteFormat) -> Result<(), PaletteError> {
pub fn to_file(&self, path: &Path, format: PaletteFormat) -> Result<(), PaletteError> {
let f = File::create(path)?;
let mut writer = BufWriter::new(f);
self.to_bytes(&mut writer, format)
@ -237,7 +242,7 @@ impl Palette {
/// * `num_colors`: the number of colors from this palette to write out to the file (<= 256)
pub fn num_colors_to_file(
&self,
path: impl AsRef<Path>,
path: &Path,
format: PaletteFormat,
num_colors: usize,
) -> Result<(), PaletteError> {
@ -290,9 +295,7 @@ impl Palette {
pub fn fade_color_toward_rgb(&mut self, color: u8, target_r: u8, target_g: u8, target_b: u8, step: u8) -> bool {
let mut modified = false;
let mut r = self.colors[color as usize].r();
let mut g = self.colors[color as usize].g();
let mut b = self.colors[color as usize].b();
let [mut r, mut g, mut b] = from_rgb32(self.colors[color as usize]);
if r != target_r {
modified = true;
@ -325,7 +328,7 @@ impl Palette {
}
if modified {
self.colors[color as usize] = RGBA::from_rgb([r, g, b]);
self.colors[color as usize] = to_rgb32([r, g, b]);
}
(target_r == r) && (target_g == g) && (target_b == b)
@ -378,7 +381,8 @@ impl Palette {
pub fn fade_colors_toward_palette<T: ColorRange>(&mut self, colors: T, palette: &Palette, step: u8) -> bool {
let mut all_faded = true;
for color in colors {
if !self.fade_color_toward_rgb(color, palette[color].r(), palette[color].g(), palette[color].b(), step) {
let [r, g, b] = from_rgb32(palette[color]);
if !self.fade_color_toward_rgb(color, r, g, b, step) {
all_faded = false;
}
}
@ -396,7 +400,7 @@ impl Palette {
/// * `t`: the amount to interpolate between the two palettes, specified as a fraction
pub fn lerp<T: ColorRange>(&mut self, colors: T, a: &Palette, b: &Palette, t: f32) {
for color in colors {
self[color] = a[color].lerp(b[color], t);
self[color] = lerp_rgb32(a[color], b[color], t);
}
}
@ -435,14 +439,15 @@ impl Palette {
let mut closest = 0;
for (index, color) in self.colors.iter().enumerate() {
if r == color.r() && g == color.g() && b == color.b() {
let [this_r, this_g, this_b] = from_rgb32(*color);
if r == this_r && g == this_g && b == this_b {
return index as u8;
} else {
// this comparison method is using the sRGB Euclidean formula described here:
// https://en.wikipedia.org/wiki/Color_difference
let distance =
abs_diff(color.r(), r) as u32 + abs_diff(color.g(), g) as u32 + abs_diff(color.b(), b) as u32;
let distance = abs_diff(this_r, r) as u32 + abs_diff(this_g, g) as u32 + abs_diff(this_b, b) as u32;
if distance < closest_distance {
closest = index as u8;
@ -469,14 +474,8 @@ impl Palette {
}
}
impl Default for Palette {
fn default() -> Self {
Self::new()
}
}
impl Index<u8> for Palette {
type Output = RGBA;
type Output = u32;
#[inline]
fn index(&self, index: u8) -> &Self::Output {
@ -503,18 +502,18 @@ mod tests {
const BASE_PATH: &str = "./test-assets/palette/";
fn test_file(file: impl AsRef<Path>) -> PathBuf {
fn test_file(file: &Path) -> PathBuf {
PathBuf::from(BASE_PATH).join(file)
}
#[test]
fn get_and_set_colors() {
let mut palette = Palette::new();
assert_eq!(RGBA::from_rgb([0, 0, 0]), palette[0]);
assert_eq!(RGBA::from_rgb([0, 0, 0]), palette[1]);
palette[0] = 0x11223344.into();
assert_eq!(RGBA::from(0x11223344), palette[0]);
assert_eq!(RGBA::from_rgb([0, 0, 0]), palette[1]);
assert_eq!(0, palette[0]);
assert_eq!(0, palette[1]);
palette[0] = 0x11223344;
assert_eq!(0x11223344, palette[0]);
assert_eq!(0, palette[1]);
}
fn assert_ega_colors(palette: &Palette) {
@ -542,7 +541,7 @@ mod tests {
// vga rgb format (6-bit)
let palette = Palette::load_from_file(test_file("vga.pal"), PaletteFormat::Vga)?;
let palette = Palette::load_from_file(test_file(Path::new("vga.pal")).as_path(), PaletteFormat::Vga)?;
assert_ega_colors(&palette);
let save_path = tmp_dir.path().join("test_save_vga_format.pal");
@ -552,7 +551,7 @@ mod tests {
// normal rgb format (8-bit)
let palette = Palette::load_from_file(test_file("dp2.pal"), PaletteFormat::Normal)?;
let palette = Palette::load_from_file(test_file(Path::new("dp2.pal")).as_path(), PaletteFormat::Normal)?;
let save_path = tmp_dir.path().join("test_save_normal_format.pal");
palette.to_file(&save_path, PaletteFormat::Normal)?;
@ -568,7 +567,8 @@ mod tests {
// vga rgb format (6-bit)
let palette = Palette::load_num_colors_from_file(test_file("ega_6bit.pal"), PaletteFormat::Vga, 16)?;
let palette =
Palette::load_num_colors_from_file(test_file(Path::new("ega_6bit.pal")).as_path(), PaletteFormat::Vga, 16)?;
assert_ega_colors(&palette);
let save_path = tmp_dir.path().join("test_save_vga_format_16_colors.pal");
@ -578,7 +578,11 @@ mod tests {
// normal rgb format (8-bit)
let palette = Palette::load_num_colors_from_file(test_file("ega_8bit.pal"), PaletteFormat::Normal, 16)?;
let palette = Palette::load_num_colors_from_file(
test_file(Path::new("ega_8bit.pal")).as_path(),
PaletteFormat::Normal,
16,
)?;
let save_path = tmp_dir.path().join("test_save_normal_format_16_colors.pal");
palette.to_file(&save_path, PaletteFormat::Normal)?;

View file

@ -21,8 +21,7 @@ mod tests {
use std::io::{BufReader, Read};
use std::path::{Path, PathBuf};
use crate::graphics::RGBA;
use crate::utils::ReadType;
use byteorder::{LittleEndian, ReadBytesExt};
#[allow(dead_code)]
const ASSETS_PATH: &str = "./assets/";
@ -30,15 +29,15 @@ mod tests {
const TEST_ASSETS_PATH: &str = "./test-assets/";
#[allow(dead_code)]
pub fn assets_file(file: impl AsRef<Path>) -> PathBuf {
pub fn assets_file(file: &Path) -> PathBuf {
PathBuf::from(ASSETS_PATH).join(file)
}
pub fn test_assets_file(file: impl AsRef<Path>) -> PathBuf {
pub fn test_assets_file(file: &Path) -> PathBuf {
PathBuf::from(TEST_ASSETS_PATH).join(file)
}
pub fn load_raw_indexed(bin_file: impl AsRef<Path>) -> Result<Box<[u8]>, io::Error> {
pub fn load_raw_indexed(bin_file: &Path) -> Result<Box<[u8]>, io::Error> {
let f = File::open(bin_file)?;
let mut reader = BufReader::new(f);
let mut buffer = Vec::new();
@ -46,12 +45,12 @@ mod tests {
Ok(buffer.into_boxed_slice())
}
pub fn load_raw_rgba(bin_file: impl AsRef<Path>) -> Result<Box<[RGBA]>, io::Error> {
pub fn load_raw_argb(bin_file: &Path) -> Result<Box<[u32]>, io::Error> {
let f = File::open(bin_file)?;
let mut reader = BufReader::new(f);
let mut buffer = Vec::new();
loop {
buffer.push(match RGBA::read(&mut reader) {
buffer.push(match reader.read_u32::<LittleEndian>() {
Ok(value) => value,
Err(err) if err.kind() == io::ErrorKind::UnexpectedEof => break,
Err(err) => return Err(err),

View file

@ -24,7 +24,8 @@ impl Circle {
let mut max_x = min_x;
let mut max_y = min_y;
for point in points.iter() {
for i in 0..points.len() {
let point = &points[i];
min_x = point.x.min(min_x);
min_y = point.y.min(min_y);
max_x = point.x.max(max_x);

View file

@ -1,6 +1,6 @@
use std::ops::{Add, Div, Mul, Sub};
use std::simd;
use std::simd::prelude::{SimdFloat, SimdPartialOrd};
use std::simd::{SimdFloat, SimdPartialOrd};
mod circle;
mod matrix3x3;
@ -268,7 +268,6 @@ mod tests {
assert!(nearly_equal(0.0, angle, 0.0001));
}
#[allow(clippy::approx_constant)]
#[test]
pub fn test_angle_to_direction() {
let (x, y) = angle_to_direction(RADIANS_0);

View file

@ -435,7 +435,6 @@ mod tests {
assert!(nearly_equal(crate::math::RIGHT, Vector2::RIGHT.angle(), 0.0001));
}
#[allow(clippy::approx_constant)]
#[test]
pub fn test_from_angle() {
let v = Vector2::from_angle(RADIANS_0);

View file

@ -445,12 +445,6 @@ impl<ContextType> States<ContextType> {
}
}
impl<ContextType> Default for States<ContextType> {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use claim::*;

View file

@ -1,6 +1,7 @@
use byte_slice_cast::AsByteSlice;
use thiserror::Error;
use crate::graphics::{ColorsAsBytes, IndexedBitmap, Palette, RgbaBitmap, RGBA};
use crate::graphics::{IndexedBitmap, Palette, RgbaBitmap};
pub fn calculate_logical_screen_size(window_width: u32, window_height: u32, scale_factor: u32) -> (u32, u32) {
let logical_width = (window_width as f32 / scale_factor as f32).ceil() as u32;
@ -23,7 +24,7 @@ pub enum SdlFramebufferError {
pub struct SdlFramebuffer {
sdl_texture: sdl2::render::Texture,
sdl_texture_pitch: usize,
intermediate_texture: Option<Box<[RGBA]>>,
intermediate_texture: Option<Box<[u32]>>,
}
// TODO: i'm not totally happy with this implementation. i don't like the two display methods and how the caller
@ -45,10 +46,11 @@ impl SdlFramebuffer {
return Err(SdlFramebufferError::SDLError(error.to_string()));
}
let format = sdl2::pixels::PixelFormatEnum::ABGR8888;
let sdl_texture =
match canvas.create_texture_streaming(Some(format), logical_screen_width, logical_screen_height) {
let sdl_texture = match canvas.create_texture_streaming(
Some(sdl2::pixels::PixelFormatEnum::ARGB8888),
logical_screen_width,
logical_screen_height,
) {
Ok(texture) => texture,
Err(error) => return Err(SdlFramebufferError::SDLError(error.to_string())),
};
@ -59,8 +61,9 @@ impl SdlFramebuffer {
// SDL texture uploads each frame. necessary as applications are dealing with 8-bit indexed
// bitmaps, not 32-bit RGBA pixels, so this temporary buffer is where we convert the final
// application framebuffer to 32-bit RGBA pixels before it is uploaded to the SDL texture
let texture_pixels_size = (logical_screen_width * logical_screen_height) as usize;
Some(vec![RGBA::default(); texture_pixels_size].into_boxed_slice())
let texture_pixels_size =
(logical_screen_width * logical_screen_height) as usize * SCREEN_TEXTURE_PIXEL_SIZE;
Some(vec![0u32; texture_pixels_size].into_boxed_slice())
} else {
None
};
@ -78,9 +81,9 @@ impl SdlFramebuffer {
"Calls to display_indexed_bitmap should only occur on SdlFramebuffers with an intermediate_texture",
);
src.copy_as_rgba_to(intermediate_texture, palette);
src.copy_as_argb_to(intermediate_texture, palette);
let texture_pixels = intermediate_texture.as_bytes();
let texture_pixels = intermediate_texture.as_byte_slice();
if let Err(error) = self.sdl_texture.update(None, texture_pixels, self.sdl_texture_pitch) {
return Err(SdlFramebufferError::SDLError(error.to_string()));
}
@ -103,7 +106,7 @@ impl SdlFramebuffer {
"Calls to display should only occur on SdlFramebuffers without an intermediate_texture"
);
let texture_pixels = src.pixels().as_bytes();
let texture_pixels = src.pixels().as_byte_slice();
if let Err(error) = self.sdl_texture.update(None, texture_pixels, self.sdl_texture_pitch) {
return Err(SdlFramebufferError::SDLError(error.to_string()));
}

View file

@ -55,12 +55,6 @@ impl Keyboard {
}
}
impl Default for Keyboard {
fn default() -> Self {
Self::new()
}
}
impl InputDevice for Keyboard {
fn update(&mut self) {
for state in self.keyboard.iter_mut() {

View file

@ -1,4 +1,4 @@
use crate::graphics::{ColorsAsBytes, GeneralBitmap, GeneralBlitMethod, IndexedBitmap, RgbaBitmap, RGBA};
use crate::graphics::{GeneralBitmap, GeneralBlitMethod, IndexedBitmap, RgbaBitmap};
use crate::math::Rect;
use crate::system::Mouse;
@ -202,16 +202,6 @@ where
}
}
impl<BitmapType> Default for CustomMouseCursor<BitmapType>
where
Self: DefaultMouseCursorBitmaps<BitmapType>,
BitmapType: GeneralBitmap,
{
fn default() -> Self {
Self::new()
}
}
impl DefaultMouseCursorBitmaps<IndexedBitmap> for CustomMouseCursor<IndexedBitmap> {
fn get_default() -> MouseCursorBitmap<IndexedBitmap> {
#[rustfmt::skip]
@ -250,34 +240,34 @@ impl DefaultMouseCursorBitmaps<IndexedBitmap> for CustomMouseCursor<IndexedBitma
impl DefaultMouseCursorBitmaps<RgbaBitmap> for CustomMouseCursor<RgbaBitmap> {
fn get_default() -> MouseCursorBitmap<RgbaBitmap> {
#[rustfmt::skip]
const CURSOR_PIXELS: [u8; DEFAULT_MOUSE_CURSOR_WIDTH * DEFAULT_MOUSE_CURSOR_HEIGHT * 4] = [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff,
0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff,
0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff,
0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff
const CURSOR_PIXELS: [u32; DEFAULT_MOUSE_CURSOR_WIDTH * DEFAULT_MOUSE_CURSOR_HEIGHT] = [
0x00000000, 0x00000000, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff,
0x00000000, 0xffffffff, 0x00000000, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff,
0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff,
0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff,
0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff,
0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff,
0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff,
0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff,
0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff,
0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff,
0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff,
0x00000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff,
0x00000000, 0x00000000, 0xffff00ff, 0xffff00ff, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff,
0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff,
0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 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(DEFAULT_MOUSE_CURSOR_WIDTH as u32, DEFAULT_MOUSE_CURSOR_HEIGHT as u32).unwrap();
cursor.pixels_mut().as_bytes_mut().copy_from_slice(&CURSOR_PIXELS);
cursor.pixels_mut().copy_from_slice(&CURSOR_PIXELS);
MouseCursorBitmap {
cursor,
hotspot_x: DEFAULT_MOUSE_CURSOR_HOTSPOT_X,
hotspot_y: DEFAULT_MOUSE_CURSOR_HOTSPOT_Y,
transparent_color: RGBA::from_rgba([255, 0, 255, 255]),
transparent_color: 0xffff00ff,
}
}
}

View file

@ -103,12 +103,6 @@ impl Mouse {
}
}
impl Default for Mouse {
fn default() -> Self {
Self::new()
}
}
impl InputDevice for Mouse {
fn update(&mut self) {
self.x_delta = 0;

View file

@ -1,7 +1,6 @@
use thiserror::Error;
use crate::audio::AudioError;
use crate::utils::app_root_dir;
mod event;
mod framebuffer;
@ -52,9 +51,6 @@ pub enum SystemError {
#[error("SystemResources error: {0}")]
SystemResourcesError(#[from] SystemResourcesError),
#[error("System I/O error")]
IOError(#[from] std::io::Error),
}
/// Builder for configuring and constructing an instance of [`System`].
@ -199,8 +195,6 @@ impl SystemBuilder {
let event_pump = SystemEventPump::from(sdl_event_pump);
let app_root_dir = app_root_dir()?;
Ok(System {
sdl_context,
sdl_audio_subsystem,
@ -208,7 +202,6 @@ impl SystemBuilder {
sdl_timer_subsystem,
res: system_resources,
event_pump,
app_root_dir,
vsync: self.vsync,
target_framerate: self.target_framerate,
target_framerate_delta: None,
@ -217,12 +210,6 @@ impl SystemBuilder {
}
}
impl Default for SystemBuilder {
fn default() -> Self {
Self::new()
}
}
/// Holds all primary structures necessary for interacting with the operating system and for
/// applications to render to the display, react to input device events, etc. through the
/// "virtual machine" exposed by this library.
@ -244,8 +231,6 @@ where
pub res: SystemResType,
pub event_pump: SystemEventPump,
pub app_root_dir: std::path::PathBuf,
}
impl<SystemResType> std::fmt::Debug for System<SystemResType>

View file

@ -1,8 +1,4 @@
use byteorder::{ReadBytesExt, WriteBytesExt};
use std::{
io::{Error, SeekFrom},
path::PathBuf,
};
use std::io::{Error, SeekFrom};
/// Provides a convenience method for determining the total size of a stream. This is provided
/// as a temporary alternative to [std::io::Seek::stream_len] which is currently marked unstable.
@ -24,41 +20,3 @@ impl<T: std::io::Read + std::io::Seek> StreamSize for T {
Ok(len)
}
}
pub trait ReadType {
type OutputType;
type ErrorType;
fn read<T: ReadBytesExt>(reader: &mut T) -> Result<Self::OutputType, Self::ErrorType>;
}
pub trait WriteType {
type ErrorType;
fn write<T: WriteBytesExt>(&self, writer: &mut T) -> Result<(), Self::ErrorType>;
}
/// Returns the application root directory (the directory that the application executable is
/// located in).
///
/// First tries to automatically detect this from the `CARGO_MANIFEST_DIR` environment variable,
/// if present, to catch scenarios where the application is running from a Cargo workspace as a
/// sub-project/binary within that workspace. In such a case, the application root directory is
/// the same as that sub-project/binary's `Cargo.toml`.
///
/// If `CARGO_MANIFEST_DIR` is not present, then an attempt is made to determine the application
/// root directory from the running executable's location.
///
/// If this fails for some reason, then this returns the current working directory.
pub fn app_root_dir() -> Result<PathBuf, Error> {
if let Some(manifest_path) = std::env::var_os("CARGO_MANIFEST_DIR") {
return Ok(PathBuf::from(manifest_path));
}
let mut exe_path = std::env::current_exe()?.canonicalize()?;
if exe_path.pop() {
return Ok(exe_path);
}
std::env::current_dir()
}

View file

@ -537,8 +537,8 @@ where
// this does mean that the size of the table is always 2 less than the number of created codes.
let mut table = vec![None; 1usize.wrapping_shl(MAX_BITS as u32)];
for (i, item) in table.iter_mut().enumerate().take(initial_table_size) {
*item = Some(vec![i as u8]);
for i in 0..initial_table_size {
table[i] = Some(vec![i as u8]);
}
let mut max_code_value_for_bit_size = get_max_code_value_for_bits(current_bit_size);
let mut next_code = initial_table_size as LzwCode + 2;

Some files were not shown because too many files have changed in this diff Show more