Compare commits
59 commits
Author | SHA1 | Date | |
---|---|---|---|
Gered | 16eb66b219 | ||
Gered | 040e480dd0 | ||
Gered | d9e26556e6 | ||
Gered | ed95332699 | ||
Gered | c7d6bd8aef | ||
Gered | 4d359f3e3c | ||
Gered | a18aee300d | ||
Gered | 0cbd5366fd | ||
Gered | 74d7f2b22c | ||
Gered | 389e27afe0 | ||
Gered | 053f95a929 | ||
Gered | f864592361 | ||
Gered | 39c88567b5 | ||
Gered | 9d02e23e9a | ||
Gered | 0f8377455c | ||
Gered | 3e88e41265 | ||
Gered | 6aabe45fbd | ||
Gered | 81de65c8d0 | ||
Gered | 3beb0f3449 | ||
Gered | 8557c24d48 | ||
Gered | d80720f772 | ||
Gered | d903ca59b3 | ||
Gered | df62205e41 | ||
Gered | ad8d1f9ae3 | ||
Gered | f5291a4d60 | ||
Gered | 4f3a629f5a | ||
Gered | afa7a92709 | ||
Gered | b03b8f4915 | ||
Gered | ec6d0f1b73 | ||
Gered | 3bb12fdcee | ||
Gered | 73e64946f7 | ||
Gered | 4257985962 | ||
Gered | f6ef1ee22e | ||
Gered | 457aa757cf | ||
Gered | 3a56926345 | ||
Gered | 78d8102a23 | ||
Gered | ae178e1fa0 | ||
Gered | 02447867a9 | ||
Gered | 09dbb913a5 | ||
Gered | cf92d42b84 | ||
Gered | 579398ea5e | ||
Gered | 0eac3ad7a3 | ||
Gered | d12d7035ca | ||
Gered | ae07b9cd37 | ||
Gered | a7567b8c0f | ||
Gered | 3b159a61b8 | ||
Gered | a0ec2f4826 | ||
Gered | 836e577bac | ||
Gered | 2843f87bdc | ||
Gered | 351a6c7e03 | ||
Gered | 34c64cd1f9 | ||
Gered | 1667ae2c43 | ||
Gered | 033081a49e | ||
Gered | 448ce11dbc | ||
Gered | 0112e99fc7 | ||
Gered | 9ba65a9e37 | ||
Gered | 53ba5cdb65 | ||
Gered | b5072bcb0f | ||
Gered | 4bef45dc6d |
|
@ -4,6 +4,14 @@ 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
|
||||
|
|
|
@ -4,5 +4,5 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "=1.0.55"
|
||||
anyhow.workspace = true
|
||||
ggdt = { path = "../../ggdt" }
|
||||
|
|
|
@ -11,15 +11,15 @@ struct AudioChannelStatus {
|
|||
playing: bool,
|
||||
}
|
||||
|
||||
fn load_and_convert_wav(path: &Path, target_spec: &AudioSpec) -> Result<AudioBuffer> {
|
||||
let sound = AudioBuffer::load_wav_file(path)?;
|
||||
fn load_and_convert_wav(path: impl AsRef<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, original_spec, final_spec);
|
||||
println!("{:?} was converted from {:?} to {:?}", path.as_ref(), original_spec, final_spec);
|
||||
} else {
|
||||
println!("{:?} did not need to be converted from {:?}", path, original_spec);
|
||||
println!("{:?} did not need to be converted from {:?}", path.as_ref(), original_spec);
|
||||
}
|
||||
Ok(sound)
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ pub struct SineWaveGenerator {
|
|||
t: usize,
|
||||
}
|
||||
|
||||
#[allow(clippy::new_without_default)]
|
||||
impl SineWaveGenerator {
|
||||
pub fn new() -> Self {
|
||||
SineWaveGenerator { t: 0 }
|
||||
|
@ -58,11 +59,11 @@ fn main() -> Result<()> {
|
|||
let mut volume = 1.0;
|
||||
|
||||
let sounds = [
|
||||
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())?,
|
||||
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())?,
|
||||
];
|
||||
|
||||
let mut statuses = [AudioChannelStatus { size: 0, position: 0, playing: false }; NUM_CHANNELS];
|
||||
|
@ -154,7 +155,7 @@ fn main() -> Result<()> {
|
|||
|
||||
for index in 0..NUM_CHANNELS {
|
||||
let channel = &audio_device[index];
|
||||
let mut status = &mut statuses[index];
|
||||
let status = &mut statuses[index];
|
||||
status.playing = channel.playing;
|
||||
status.position = channel.position;
|
||||
status.size = channel.data.len();
|
||||
|
@ -183,8 +184,7 @@ fn main() -> Result<()> {
|
|||
system.res.video.print_string("Audio Channels", 16, 32, FontRenderOpts::Color(14), &system.res.font);
|
||||
|
||||
let mut y = 48;
|
||||
for index in 0..NUM_CHANNELS {
|
||||
let status = &statuses[index];
|
||||
for (index, status) in statuses.iter().enumerate() {
|
||||
system.res.video.print_string(
|
||||
&format!(
|
||||
"channel {} - {} {}",
|
||||
|
|
|
@ -4,5 +4,5 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "=1.0.55"
|
||||
anyhow.workspace = true
|
||||
ggdt = { path = "../../ggdt" }
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use ggdt::prelude::*;
|
||||
|
@ -26,7 +24,7 @@ fn main() -> Result<()> {
|
|||
|
||||
let font = BitmaskFont::new_vga_font()?;
|
||||
|
||||
let (balls_bmp, balls_palette) = IndexedBitmap::load_pcx_file(Path::new("./assets/balls.pcx"))?;
|
||||
let (balls_bmp, balls_palette) = IndexedBitmap::load_pcx_file("./assets/balls.pcx")?;
|
||||
system.res.palette = balls_palette.clone();
|
||||
|
||||
let mut sprites = Vec::<IndexedBitmap>::new();
|
||||
|
@ -62,8 +60,7 @@ fn main() -> Result<()> {
|
|||
}
|
||||
|
||||
if system.res.keyboard.is_key_up(Scancode::S) {
|
||||
for i in 0..NUM_BALLS {
|
||||
let ball = &mut balls[i];
|
||||
for ball in balls.iter_mut() {
|
||||
ball.x += ball.dir_x;
|
||||
ball.y += ball.dir_y;
|
||||
|
||||
|
@ -72,11 +69,9 @@ fn main() -> Result<()> {
|
|||
ball.dir_x = -ball.dir_x;
|
||||
ball.x = 0;
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
} 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 {
|
||||
|
@ -84,11 +79,9 @@ fn main() -> Result<()> {
|
|||
ball.dir_y = -ball.dir_y;
|
||||
ball.y = 0;
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,5 +4,5 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "=1.0.55"
|
||||
anyhow.workspace = true
|
||||
ggdt = { path = "../../ggdt", features = [] }
|
||||
|
|
Binary file not shown.
|
@ -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 mut position = positions.get_mut(entity).unwrap();
|
||||
let mut velocity = velocities.get_mut(entity).unwrap();
|
||||
let position = positions.get_mut(entity).unwrap();
|
||||
let 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 {
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use ggdt::prelude::*;
|
||||
|
|
|
@ -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(Path::new("./assets/balls.pcx"))?;
|
||||
let (balls_bmp, balls_palette) = IndexedBitmap::load_pcx_file("./assets/balls.pcx")?;
|
||||
system.res.palette = balls_palette.clone();
|
||||
|
||||
let mut sprites = Vec::new();
|
||||
|
|
|
@ -4,9 +4,9 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "=1.0.55"
|
||||
anyhow.workspace = true
|
||||
ggdt = { path = "../../ggdt" }
|
||||
ggdt_imgui = { path = "../../ggdt_imgui" }
|
||||
imgui = "0.11.0"
|
||||
serde = { version = "1.0.136", features = ["derive"] }
|
||||
serde_json = "1.0.79"
|
||||
imgui.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
use crate::entities::{AnimationDef, EntityActivity, Event};
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use ggdt::prelude::*;
|
||||
|
||||
use crate::entities::{AnimationDef, EntityActivity, Event, SlimeColor};
|
||||
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: u32,
|
||||
pub transparent_color: RGBA,
|
||||
pub system: System<Standard>,
|
||||
pub palette: Palette,
|
||||
pub font: BitmaskFont,
|
||||
|
@ -25,6 +27,7 @@ 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 {
|
||||
|
@ -87,23 +90,31 @@ impl AppContext<Standard> for GameContext {
|
|||
|
||||
impl GameContext {
|
||||
pub fn new(system: System<Standard>) -> Result<Self> {
|
||||
let palette = load_palette(Path::new("./assets/db16.pal"))?;
|
||||
let palette = load_palette("./assets/db16.pal")?;
|
||||
|
||||
let font = load_font(Path::new("./assets/dp.fnt"))?;
|
||||
let small_font = load_font(Path::new("./assets/small.fnt"))?;
|
||||
let font = load_font("./assets/dp.fnt")?;
|
||||
let small_font = load_font("./assets/small.fnt")?;
|
||||
|
||||
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 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 tilemap = TileMap::load_from(Path::new("./assets/arena.map.json"))?;
|
||||
let tilemap = TileMap::load_from("./assets/arena.map.json")?;
|
||||
|
||||
let entities = Entities::new();
|
||||
let component_systems = ComponentSystems::new();
|
||||
let event_publisher = EventPublisher::new();
|
||||
let event_listeners = EventListeners::new();
|
||||
let imgui = ggdt_imgui::ImGui::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 slime_activity_states = HashMap::from([
|
||||
(EntityActivity::Idle, Rc::new(AnimationDef::new(&[1, 2], true, 1.0, Some(3)))),
|
||||
|
@ -129,6 +140,7 @@ 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 },
|
||||
})
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
use crate::context::{CoreContext, GameContext};
|
||||
use crate::tilemap::TileMap;
|
||||
use ggdt::prelude::*;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
use ggdt::prelude::*;
|
||||
|
||||
use crate::context::{CoreContext, GameContext};
|
||||
use crate::tilemap::TileMap;
|
||||
|
||||
pub const FRICTION: f32 = 0.5;
|
||||
pub const DEFAULT_PUSH_STRENGTH: f32 = 0.5;
|
||||
pub const DEFAULT_PUSH_DISSIPATION: f32 = 0.5;
|
||||
|
@ -211,7 +213,7 @@ impl RandomlyWalksAround {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct Slime;
|
||||
pub struct Slime(pub SlimeColor);
|
||||
|
||||
pub struct Sprite {
|
||||
pub atlas: Rc<BitmapAtlas<RgbaBitmap>>,
|
||||
|
@ -367,7 +369,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 mut activity = activities.get_mut(&entity).unwrap();
|
||||
let 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
|
||||
|
@ -738,7 +740,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);
|
||||
context.entities.add_component(id, Slime(color));
|
||||
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());
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
mod context;
|
||||
mod entities;
|
||||
mod support;
|
||||
mod tilemap;
|
||||
|
||||
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};
|
||||
use ggdt::prelude::*;
|
||||
use ggdt_imgui::UiSupport;
|
||||
|
||||
mod context;
|
||||
mod entities;
|
||||
mod support;
|
||||
mod tilemap;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct DemoState {
|
||||
|
@ -27,17 +28,22 @@ 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([200.0, 200.0], imgui::Condition::FirstUseEver)
|
||||
.size([240.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, _) in context.core.entities.components::<Slime>().unwrap().iter() {
|
||||
for (slime, slime_type) 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);
|
||||
|
@ -79,11 +85,12 @@ impl AppState<GameContext> for DemoState {
|
|||
}
|
||||
});
|
||||
|
||||
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;
|
||||
}
|
||||
if !ui.is_any_hovered()
|
||||
&& !ui.is_any_focused()
|
||||
&& 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);
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
use std::path::Path;
|
||||
|
||||
use crate::tilemap::{TILE_HEIGHT, TILE_WIDTH};
|
||||
use anyhow::{Context, Result};
|
||||
use ggdt::prelude::*;
|
||||
|
||||
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_palette(path: impl AsRef<Path>) -> Result<Palette> {
|
||||
Palette::load_from_file(&path, PaletteFormat::Vga).context(format!("Loading palette: {:?}", 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_font(path: impl AsRef<Path>) -> Result<BitmaskFont> {
|
||||
BitmaskFont::load_from_file(&path).context(format!("Loading font: {:?}", 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))?;
|
||||
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()))?;
|
||||
let mut atlas = BitmapAtlas::new(bmp);
|
||||
atlas.add_grid(TILE_WIDTH, TILE_HEIGHT)?;
|
||||
Ok(atlas)
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use std::path::Path;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use ggdt::prelude::*;
|
||||
|
||||
|
@ -16,10 +18,10 @@ pub struct TileMap {
|
|||
}
|
||||
|
||||
impl TileMap {
|
||||
pub fn load_from(path: &std::path::Path) -> Result<Self> {
|
||||
let f = std::fs::File::open(path)?;
|
||||
pub fn load_from(path: impl AsRef<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))
|
||||
serde_json::from_reader(reader).context(format!("Loading json tilemap: {:?}", path.as_ref()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -63,7 +65,7 @@ impl TileMap {
|
|||
tiles: &BitmapAtlas<RgbaBitmap>,
|
||||
camera_x: i32,
|
||||
camera_y: i32,
|
||||
transparent_color: u32,
|
||||
transparent_color: RGBA,
|
||||
) {
|
||||
let xt = camera_x / TILE_WIDTH as i32;
|
||||
let yt = camera_y / TILE_HEIGHT as i32;
|
||||
|
|
|
@ -4,8 +4,8 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "=1.0.55"
|
||||
anyhow.workspace = true
|
||||
ggdt = { path = "../../ggdt", features = [] }
|
||||
serde = { version = "1.0.136", features = ["derive"] }
|
||||
serde_json = "1.0.79"
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
{
|
||||
"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.
|
@ -381,7 +381,7 @@ pub struct Pickuper;
|
|||
|
||||
pub fn init_everything(
|
||||
context: &mut Game,
|
||||
map_file: &Path,
|
||||
map_file: impl AsRef<Path>,
|
||||
min_spawn_time: f32,
|
||||
max_spawn_time: f32,
|
||||
max_slimes: usize,
|
||||
|
|
|
@ -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 mut activity = activities.get_mut(&entity).unwrap();
|
||||
let 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((_, mut camera)) = context.entities.components_mut::<Camera>().single_mut() {
|
||||
if let Some((_, 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(mut facing_direction) = facing_directions.get_mut(&attachment.0) {
|
||||
if let Some(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(mut attachment_position) = positions.get_mut(&attached_entity) {
|
||||
if let Some(attachment_position) = positions.get_mut(&attached_entity) {
|
||||
// start off the attachment by placing it directly at the parent
|
||||
attachment_position.0 = parent_position;
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
extern crate core;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
use std::rc::Rc;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
|
@ -108,30 +107,30 @@ impl AppContext<DosLike> for Game {
|
|||
|
||||
impl Game {
|
||||
pub fn new(mut system: System<DosLike>) -> Result<Self> {
|
||||
let palette = load_palette(Path::new("./assets/db16.pal"))?;
|
||||
let palette = load_palette("./assets/db16.pal")?;
|
||||
system.res.palette = palette.clone();
|
||||
|
||||
let font = load_font(Path::new("./assets/dp.fnt"))?;
|
||||
let font = load_font("./assets/dp.fnt")?;
|
||||
|
||||
let tiles = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/tiles.pcx"))?);
|
||||
let hero_male = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/hero_male.pcx"))?);
|
||||
let hero_female = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/hero_female.pcx"))?);
|
||||
let green_slime = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/green_slime.pcx"))?);
|
||||
let blue_slime = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/blue_slime.pcx"))?);
|
||||
let orange_slime = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/orange_slime.pcx"))?);
|
||||
let fist = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/fist.pcx"))?);
|
||||
let sword = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/sword.pcx"))?);
|
||||
let particles = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/particles.pcx"))?);
|
||||
let items = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/items.pcx"))?);
|
||||
let tiles = Rc::new(load_bitmap_atlas_autogrid("./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 mut ui = load_bitmap_atlas(Path::new("./assets/ui.pcx"))?;
|
||||
let mut ui = load_bitmap_atlas("./assets/ui.pcx")?;
|
||||
ui.add(Rect::new(0, 0, 16, 16))?;
|
||||
ui.add(Rect::new(16, 0, 16, 16))?;
|
||||
for i in 0..8 {
|
||||
ui.add(Rect::new(i * 8, 16, 8, 8))?;
|
||||
}
|
||||
|
||||
let tilemap = TileMap::load_from(Path::new("./assets/title_screen.map.json"))?;
|
||||
let tilemap = TileMap::load_from("./assets/title_screen.map.json")?;
|
||||
|
||||
let entities = Entities::new();
|
||||
let component_systems = ComponentSystems::new();
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
use std::path::Path;
|
||||
|
||||
use ggdt::prelude::*;
|
||||
|
||||
use crate::entities::*;
|
||||
|
@ -45,7 +43,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);
|
||||
|
||||
|
@ -85,10 +83,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, Path::new("./assets/title_screen.map.json"), 0.2, 1.0, 32);
|
||||
init_everything(context, "./assets/title_screen.map.json", 0.2, 1.0, 32);
|
||||
}
|
||||
State::TransitionIn => {
|
||||
self.fade = 0.0;
|
||||
|
@ -172,7 +170,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);
|
||||
}
|
||||
|
@ -216,10 +214,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, Path::new("./assets/arena.map.json"), 0.5, 2.0, 100);
|
||||
init_everything(context, "./assets/arena.map.json", 0.5, 2.0, 100);
|
||||
spawn_player_randomly(&mut context.core);
|
||||
}
|
||||
State::TransitionIn => {
|
||||
|
|
|
@ -6,23 +6,23 @@ use ggdt::prelude::*;
|
|||
|
||||
use crate::{Game, TILE_HEIGHT, TILE_WIDTH};
|
||||
|
||||
pub fn load_palette(path: &Path) -> Result<Palette> {
|
||||
Palette::load_from_file(path, PaletteFormat::Vga).context(format!("Loading palette: {:?}", path))
|
||||
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_font(path: &Path) -> Result<BitmaskFont> {
|
||||
BitmaskFont::load_from_file(path).context(format!("Loading font: {:?}", 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_bitmap_atlas_autogrid(path: &Path) -> Result<BitmapAtlas<IndexedBitmap>> {
|
||||
let (bmp, _) = IndexedBitmap::load_file(path).context(format!("Loading bitmap atlas: {:?}", 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()))?;
|
||||
let mut atlas = BitmapAtlas::new(bmp);
|
||||
atlas.add_grid(TILE_WIDTH, TILE_HEIGHT)?;
|
||||
Ok(atlas)
|
||||
}
|
||||
|
||||
pub fn load_bitmap_atlas(path: &Path) -> Result<BitmapAtlas<IndexedBitmap>> {
|
||||
let (bmp, _) = IndexedBitmap::load_file(path).context(format!("Loading bitmap atlas: {:?}", path))?;
|
||||
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()))?;
|
||||
let atlas = BitmapAtlas::new(bmp);
|
||||
Ok(atlas)
|
||||
}
|
||||
|
|
|
@ -21,10 +21,10 @@ pub struct TileMap {
|
|||
}
|
||||
|
||||
impl TileMap {
|
||||
pub fn load_from(path: &Path) -> Result<Self> {
|
||||
let f = File::open(path)?;
|
||||
pub fn load_from(path: impl AsRef<Path>) -> Result<Self> {
|
||||
let f = File::open(&path)?;
|
||||
let reader = BufReader::new(f);
|
||||
serde_json::from_reader(reader).context(format!("Loading json tilemap: {:?}", path))
|
||||
serde_json::from_reader(reader).context(format!("Loading json tilemap: {:?}", path.as_ref()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
|
@ -4,5 +4,5 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "=1.0.55"
|
||||
anyhow.workspace = true
|
||||
ggdt = { path = "../../ggdt" }
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -4,5 +4,5 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "=1.0.55"
|
||||
anyhow.workspace = true
|
||||
ggdt = { path = "../../ggdt" }
|
||||
|
|
|
@ -6,16 +6,18 @@ authors = ["Gered King <gered@blarg.ca>"]
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
byte-slice-cast = "1.2.1"
|
||||
byteorder = "1.4.3"
|
||||
thiserror = "=1.0.30"
|
||||
byte-slice-cast = "1.2.2"
|
||||
byteorder = "1.5.0"
|
||||
thiserror.workspace = true
|
||||
rand = "0.8.5"
|
||||
num = "0.4.0"
|
||||
num-derive = "0.3.3"
|
||||
num-traits = "0.2.15"
|
||||
bitflags = "2.1.0"
|
||||
flate2 = "1.0.25"
|
||||
num = "0.4.1"
|
||||
num-derive = "0.4.1"
|
||||
num-traits = "0.2.17"
|
||||
bitflags = "2.4.1"
|
||||
flate2 = "=1.0.27"
|
||||
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"] }
|
||||
|
@ -25,9 +27,9 @@ sdl2 = { git = "https://github.com/Rust-SDL2/rust-sdl2/", rev = "819ab438ac971a9
|
|||
|
||||
[dev-dependencies]
|
||||
claim = "0.5.0"
|
||||
criterion = "0.4.0"
|
||||
anyhow = "=1.0.55"
|
||||
tempfile = "3.4.0"
|
||||
criterion = "0.5.1"
|
||||
anyhow.workspace = true
|
||||
tempfile = "3.8.1"
|
||||
|
||||
[[bench]]
|
||||
name = "bitmap"
|
||||
|
@ -41,6 +43,19 @@ 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"
|
||||
|
||||
|
|
|
@ -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![0u32; (width * height * 4) as usize].into_boxed_slice();
|
||||
let mut dest = vec![RGBA::default(); (width * height) as usize].into_boxed_slice();
|
||||
let palette = Palette::new_vga_palette().unwrap();
|
||||
|
||||
c.bench_function("deindex_bitmap_pixels", |b| b.iter(|| source.copy_as_argb_to(&mut dest, &palette)));
|
||||
c.bench_function("deindex_bitmap_pixels", |b| b.iter(|| source.copy_as_rgba_to(&mut dest, &palette)));
|
||||
|
||||
c.bench_function("set_pixel", |b| b.iter(|| source.set_pixel(black_box(100), black_box(100), black_box(42))));
|
||||
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
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(Path::new("./test-assets/test-tiles.lbm")).unwrap();
|
||||
let (bmp, _) = IndexedBitmap::load_iff_file("./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);
|
||||
|
|
163
ggdt/benches/primitives.rs
Normal file
163
ggdt/benches/primitives.rs
Normal file
|
@ -0,0 +1,163 @@
|
|||
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);
|
|
@ -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(std::path::Path::new("./test-assets/gif/small.gif")).unwrap();
|
||||
let (texture, _) = RgbaBitmap::load_gif_file("./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,19 +22,20 @@ 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_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]);
|
||||
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]);
|
||||
|
||||
c.bench_function("rgbabitmap_triangle_2d_solid_color", |b| {
|
||||
let triangle = RgbaTriangle2d::Solid { position: [big_v1, big_v2, big_v3], color: 5 };
|
||||
let triangle = RgbaTriangle2d::Solid { position: [big_v1, big_v2, big_v3], color };
|
||||
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: 5 };
|
||||
let triangle = RgbaTriangle2d::Solid { position: [small_v1, small_v2, small_v3], color };
|
||||
b.iter(|| {
|
||||
dest.triangle_2d(black_box(&triangle));
|
||||
})
|
||||
|
|
|
@ -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: &Path) -> Result<AudioBuffer, WavError> {
|
||||
pub fn load_wav_file(path: impl AsRef<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: &Path) -> PathBuf {
|
||||
fn test_file(file: impl AsRef<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(Path::new("22khz_8bit_1ch.wav")).as_path())?;
|
||||
let wav_buffer = AudioBuffer::load_wav_file(test_file("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(Path::new("44khz_8bit_1ch.wav")).as_path())?;
|
||||
let wav_buffer = AudioBuffer::load_wav_file(test_file("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(Path::new("22khz_16bit_1ch.wav")).as_path())?;
|
||||
let wav_buffer = AudioBuffer::load_wav_file(test_file("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(Path::new("44khz_16bit_1ch.wav")).as_path())?;
|
||||
let wav_buffer = AudioBuffer::load_wav_file(test_file("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);
|
||||
|
|
|
@ -174,6 +174,12 @@ impl AudioChannel {
|
|||
}
|
||||
}
|
||||
|
||||
impl Default for AudioChannel {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
|
|
|
@ -2,8 +2,6 @@ use sdl2::audio::{AudioFormat, AudioFormatNum, AudioSpecDesired};
|
|||
use sdl2::AudioSubsystem;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::audio::AudioDevice;
|
||||
|
||||
mod buffer;
|
||||
mod device;
|
||||
mod queue;
|
||||
|
|
|
@ -1,133 +1,135 @@
|
|||
//! 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;
|
||||
|
||||
|
|
|
@ -230,6 +230,12 @@ 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 ... ?
|
||||
|
@ -470,6 +476,12 @@ impl<U, R> ComponentSystems<U, R> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<U, R> Default for ComponentSystems<U, R> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -667,7 +679,7 @@ mod tests {
|
|||
// modify position components
|
||||
{
|
||||
let mut positions = em.components_mut::<Position>().unwrap();
|
||||
for mut component in positions.values_mut() {
|
||||
for component in positions.values_mut() {
|
||||
component.0 += 5;
|
||||
}
|
||||
|
||||
|
@ -678,7 +690,7 @@ mod tests {
|
|||
// modify health components
|
||||
{
|
||||
let mut healths = em.components_mut::<Health>().unwrap();
|
||||
for mut component in healths.values_mut() {
|
||||
for component in healths.values_mut() {
|
||||
component.0 += 5;
|
||||
}
|
||||
assert_eq!(Health(25), *healths.get(&a).unwrap());
|
||||
|
@ -722,10 +734,10 @@ mod tests {
|
|||
|
||||
println!("entity {}, health: {:?}, position: {:?}", name.0, health, position);
|
||||
|
||||
if let Some(mut health) = health {
|
||||
if let Some(health) = health {
|
||||
health.0 += 5;
|
||||
}
|
||||
if let Some(mut position) = position {
|
||||
if let Some(position) = position {
|
||||
position.0 += 5;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,6 +55,12 @@ 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.
|
||||
///
|
||||
|
@ -150,6 +156,12 @@ 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::*;
|
||||
|
@ -175,10 +187,12 @@ 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
|
||||
}
|
||||
|
||||
|
|
|
@ -127,6 +127,10 @@ 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>,
|
||||
|
@ -153,6 +157,10 @@ 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>,
|
||||
|
@ -183,6 +191,10 @@ 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>,
|
||||
|
@ -280,6 +292,10 @@ 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;
|
||||
|
@ -294,6 +310,10 @@ 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,
|
||||
|
@ -317,6 +337,10 @@ 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,
|
||||
|
@ -339,6 +363,10 @@ 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,
|
||||
|
@ -365,6 +393,10 @@ 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,
|
||||
|
@ -388,6 +420,10 @@ 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,
|
||||
|
@ -415,6 +451,10 @@ 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,
|
||||
|
@ -440,6 +480,10 @@ 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,
|
||||
|
|
|
@ -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,
|
||||
BitmapError, Font, FontRenderOpts, IndexedBitmap, IndexedBlitMethod, Pixel, RgbaBitmap, RgbaBlitMethod, RGBA,
|
||||
};
|
||||
use crate::math::Rect;
|
||||
|
||||
|
@ -220,7 +220,7 @@ impl GeneralBitmap for IndexedBitmap {
|
|||
}
|
||||
|
||||
impl GeneralBitmap for RgbaBitmap {
|
||||
type PixelType = u32;
|
||||
type PixelType = RGBA;
|
||||
|
||||
#[inline]
|
||||
fn new(width: u32, height: u32) -> Result<Self, BitmapError> {
|
||||
|
|
|
@ -468,7 +468,7 @@ impl IndexedBitmap {
|
|||
Ok((bitmap.unwrap(), palette.unwrap()))
|
||||
}
|
||||
|
||||
pub fn load_gif_file(path: &Path) -> Result<(IndexedBitmap, Palette), GifError> {
|
||||
pub fn load_gif_file(path: impl AsRef<Path>) -> Result<(IndexedBitmap, Palette), GifError> {
|
||||
let f = File::open(path)?;
|
||||
let mut reader = BufReader::new(f);
|
||||
Self::load_gif_bytes(&mut reader)
|
||||
|
@ -520,7 +520,12 @@ impl IndexedBitmap {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn to_gif_file(&self, path: &Path, palette: &Palette, settings: GifSettings) -> Result<(), GifError> {
|
||||
pub fn to_gif_file(
|
||||
&self,
|
||||
path: impl AsRef<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)
|
||||
|
@ -537,7 +542,7 @@ impl RgbaBitmap {
|
|||
Ok((output, palette))
|
||||
}
|
||||
|
||||
pub fn load_gif_file(path: &Path) -> Result<(RgbaBitmap, Palette), GifError> {
|
||||
pub fn load_gif_file(path: impl AsRef<Path>) -> Result<(RgbaBitmap, Palette), GifError> {
|
||||
let (temp_bitmap, palette) = IndexedBitmap::load_gif_file(path)?;
|
||||
let output = temp_bitmap.to_rgba(&palette);
|
||||
Ok((output, palette))
|
||||
|
@ -556,7 +561,7 @@ mod tests {
|
|||
|
||||
const BASE_PATH: &str = "./test-assets/gif/";
|
||||
|
||||
fn test_file(file: &Path) -> PathBuf {
|
||||
fn test_file(file: impl AsRef<Path>) -> PathBuf {
|
||||
PathBuf::from(BASE_PATH).join(file)
|
||||
}
|
||||
|
||||
|
@ -564,14 +569,14 @@ mod tests {
|
|||
fn load_and_save() -> Result<(), GifError> {
|
||||
let tmp_dir = TempDir::new()?;
|
||||
|
||||
let ref_pixels = load_raw_indexed(test_file(Path::new("small.bin")).as_path())?;
|
||||
let ref_pixels = load_raw_indexed(test_file("small.bin"))?;
|
||||
let dp2_palette = Palette::load_from_file(
|
||||
test_assets_file(Path::new("dp2.pal")).as_path(), //
|
||||
test_assets_file("dp2.pal"), //
|
||||
PaletteFormat::Normal,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let (bmp, palette) = IndexedBitmap::load_gif_file(test_file(Path::new("small.gif")).as_path())?;
|
||||
let (bmp, palette) = IndexedBitmap::load_gif_file(test_file("small.gif"))?;
|
||||
assert_eq!(16, bmp.width());
|
||||
assert_eq!(16, bmp.height());
|
||||
assert_eq!(bmp.pixels(), ref_pixels.as_ref());
|
||||
|
@ -597,9 +602,9 @@ mod tests {
|
|||
|
||||
// first image
|
||||
|
||||
let ref_pixels = load_raw_indexed(test_file(Path::new("large_1.bin")).as_path())?;
|
||||
let ref_pixels = load_raw_indexed(test_file("large_1.bin"))?;
|
||||
|
||||
let (bmp, palette) = IndexedBitmap::load_gif_file(test_file(Path::new("large_1.gif")).as_path())?;
|
||||
let (bmp, palette) = IndexedBitmap::load_gif_file(test_file("large_1.gif"))?;
|
||||
assert_eq!(320, bmp.width());
|
||||
assert_eq!(200, bmp.height());
|
||||
assert_eq!(bmp.pixels(), ref_pixels.as_ref());
|
||||
|
@ -613,9 +618,9 @@ mod tests {
|
|||
|
||||
// second image
|
||||
|
||||
let ref_pixels = load_raw_indexed(test_file(Path::new("large_2.bin")).as_path())?;
|
||||
let ref_pixels = load_raw_indexed(test_file("large_2.bin"))?;
|
||||
|
||||
let (bmp, palette) = IndexedBitmap::load_gif_file(test_file(Path::new("large_2.gif")).as_path())?;
|
||||
let (bmp, palette) = IndexedBitmap::load_gif_file(test_file("large_2.gif"))?;
|
||||
assert_eq!(320, bmp.width());
|
||||
assert_eq!(200, bmp.height());
|
||||
assert_eq!(bmp.pixels(), ref_pixels.as_ref());
|
||||
|
|
|
@ -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 x in 0..row_size {
|
||||
for dest_pixel in dest.iter_mut().take(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[x] = data;
|
||||
*dest_pixel = data;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -430,7 +430,7 @@ impl IndexedBitmap {
|
|||
Ok((bitmap.unwrap(), palette.unwrap()))
|
||||
}
|
||||
|
||||
pub fn load_iff_file(path: &Path) -> Result<(IndexedBitmap, Palette), IffError> {
|
||||
pub fn load_iff_file(path: impl AsRef<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: &Path, palette: &Palette, format: IffFormat) -> Result<(), IffError> {
|
||||
pub fn to_iff_file(&self, path: impl AsRef<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: &Path) -> Result<(RgbaBitmap, Palette), IffError> {
|
||||
pub fn load_iff_file(path: impl AsRef<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: &Path) -> PathBuf {
|
||||
fn test_file(file: impl AsRef<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(Path::new("small.bin")).as_path())?;
|
||||
let ref_pixels = load_raw_indexed(test_file("small.bin"))?;
|
||||
let dp2_palette = Palette::load_from_file(
|
||||
test_assets_file(Path::new("dp2.pal")).as_path(), //
|
||||
test_assets_file("dp2.pal"), //
|
||||
PaletteFormat::Normal,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// ILBM format
|
||||
|
||||
let (bmp, palette) = IndexedBitmap::load_iff_file(test_file(Path::new("small.lbm")).as_path())?;
|
||||
let (bmp, palette) = IndexedBitmap::load_iff_file(test_file("small.lbm"))?;
|
||||
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(Path::new("small.pbm")).as_path())?;
|
||||
let (bmp, palette) = IndexedBitmap::load_iff_file(test_file("small.pbm"))?;
|
||||
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(Path::new("large_1.bin")).as_path())?;
|
||||
let ref_pixels = load_raw_indexed(test_file("large_1.bin"))?;
|
||||
|
||||
let (bmp, palette) = IndexedBitmap::load_iff_file(test_file(Path::new("large_1.pbm")).as_path())?;
|
||||
let (bmp, palette) = IndexedBitmap::load_iff_file(test_file("large_1.pbm"))?;
|
||||
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(Path::new("large_1.lbm")).as_path())?;
|
||||
let (bmp, palette) = IndexedBitmap::load_iff_file(test_file("large_1.lbm"))?;
|
||||
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(Path::new("large_2.bin")).as_path())?;
|
||||
let ref_pixels = load_raw_indexed(test_file("large_2.bin"))?;
|
||||
|
||||
let (bmp, palette) = IndexedBitmap::load_iff_file(test_file(Path::new("large_2.lbm")).as_path())?;
|
||||
let (bmp, palette) = IndexedBitmap::load_iff_file(test_file("large_2.lbm"))?;
|
||||
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(Path::new("large_2.lbm")).as_path())?;
|
||||
let (bmp, palette) = IndexedBitmap::load_iff_file(test_file("large_2.lbm"))?;
|
||||
assert_eq!(320, bmp.width());
|
||||
assert_eq!(200, bmp.height());
|
||||
assert_eq!(bmp.pixels(), ref_pixels.as_ref());
|
||||
|
|
|
@ -126,6 +126,10 @@ 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,
|
||||
|
@ -150,6 +154,10 @@ 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,
|
||||
|
@ -178,6 +186,10 @@ 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,
|
||||
|
@ -198,6 +210,10 @@ 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,
|
||||
|
@ -222,6 +238,10 @@ 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,
|
||||
|
@ -249,6 +269,10 @@ 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,
|
||||
|
@ -280,6 +304,10 @@ 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,
|
||||
|
@ -303,6 +331,10 @@ 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,
|
||||
|
@ -330,6 +362,10 @@ 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,
|
||||
|
@ -363,6 +399,10 @@ 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,
|
||||
|
@ -399,6 +439,10 @@ 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,
|
||||
|
@ -426,6 +470,10 @@ 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,
|
||||
|
@ -522,6 +570,10 @@ 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(
|
||||
|
@ -606,12 +658,20 @@ 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,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::path::Path;
|
||||
|
||||
use crate::graphics::{Bitmap, BitmapError, Palette, RgbaBitmap};
|
||||
use crate::graphics::{Bitmap, BitmapError, Palette, RgbaBitmap, RGBA};
|
||||
|
||||
mod blit;
|
||||
mod primitives;
|
||||
|
@ -25,8 +25,8 @@ impl IndexedBitmap {
|
|||
Self::internal_new(width, height, 0)
|
||||
}
|
||||
|
||||
pub fn load_file(path: &Path) -> Result<(Self, Palette), BitmapError> {
|
||||
if let Some(extension) = path.extension() {
|
||||
pub fn load_file(path: impl AsRef<Path>) -> Result<(Self, Palette), BitmapError> {
|
||||
if let Some(extension) = path.as_ref().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 ARGB-format pixel data. This can be used to display the contents of the bitmap
|
||||
/// 32-bit RGBA-format pixel data. This can be used to display the contents of the bitmap
|
||||
/// on-screen by using an SDL Surface, OpenGL texture, etc as the destination.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `dest`: destination 32-bit ARGB pixel buffer to copy converted pixels to
|
||||
/// * `dest`: destination 32-bit RGBA pixel buffer to copy converted pixels to
|
||||
/// * `palette`: the 256 colour palette to use during pixel conversion
|
||||
pub fn copy_as_argb_to(&self, dest: &mut [u32], palette: &Palette) {
|
||||
pub fn copy_as_rgba_to(&self, dest: &mut [RGBA], 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_argb_to(output.pixels_mut(), palette);
|
||||
self.copy_as_rgba_to(output.pixels_mut(), palette);
|
||||
output
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,8 +20,11 @@ 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. The
|
||||
/// 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.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// 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) {
|
||||
|
@ -40,6 +43,7 @@ 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(
|
||||
|
@ -56,6 +60,7 @@ 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(
|
||||
|
@ -71,6 +76,7 @@ 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(
|
||||
|
@ -88,6 +94,7 @@ 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(
|
||||
|
@ -106,6 +113,7 @@ 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(
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -212,8 +212,12 @@ 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. The 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.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// 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);
|
||||
|
@ -221,8 +225,12 @@ 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. The 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.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// 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);
|
||||
|
@ -236,10 +244,10 @@ impl<PixelType: Pixel> Bitmap<PixelType> {
|
|||
/// coordinates. If the coordinates given are outside the bitmap's current clipping region,
|
||||
/// None is returned.
|
||||
#[inline]
|
||||
pub unsafe fn pixels_at_ptr(&self, x: i32, y: i32) -> Option<*const PixelType> {
|
||||
pub 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(self.pixels.as_ptr().add(offset))
|
||||
Some(unsafe { self.pixels.as_ptr().add(offset) })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -249,18 +257,22 @@ impl<PixelType: Pixel> Bitmap<PixelType> {
|
|||
/// given coordinates. If the coordinates given are outside the bitmap's current clipping
|
||||
/// region, None is returned.
|
||||
#[inline]
|
||||
pub unsafe fn pixels_at_mut_ptr(&mut self, x: i32, y: i32) -> Option<*mut PixelType> {
|
||||
pub 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(self.pixels.as_mut_ptr().add(offset))
|
||||
Some(unsafe { self.pixels.as_mut_ptr().add(offset) })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an unsafe pointer to the subset of the raw pixels in this bitmap beginning at the
|
||||
/// given coordinates. The coordinates are not checked for validity, so it is up to you to
|
||||
/// ensure they lie within the bounds of the bitmap.
|
||||
/// 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.
|
||||
#[inline]
|
||||
pub unsafe fn pixels_at_ptr_unchecked(&self, x: i32, y: i32) -> *const PixelType {
|
||||
let offset = self.get_offset_to_xy(x, y);
|
||||
|
@ -268,8 +280,12 @@ 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. The 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.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// 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);
|
||||
|
@ -482,15 +498,15 @@ mod tests {
|
|||
let mut bmp = Bitmap::<u8>::new(8, 8).unwrap();
|
||||
bmp.pixels_mut().copy_from_slice(RAW_BMP_PIXELS);
|
||||
|
||||
assert_eq!(None, unsafe { bmp.pixels_at_ptr(-1, -1) });
|
||||
assert_eq!(None, bmp.pixels_at_ptr(-1, -1));
|
||||
|
||||
let offset = bmp.get_offset_to_xy(1, 1);
|
||||
let pixels = unsafe { bmp.pixels_at_ptr(0, 0).unwrap() };
|
||||
let pixels = bmp.pixels_at_ptr(0, 0).unwrap();
|
||||
assert_eq!(0, unsafe { *pixels });
|
||||
assert_eq!(1, unsafe { *(pixels.add(offset)) });
|
||||
assert_eq!(2, unsafe { *(pixels.add(63)) });
|
||||
|
||||
let pixels = unsafe { bmp.pixels_at_ptr(1, 1).unwrap() };
|
||||
let pixels = bmp.pixels_at_ptr(1, 1).unwrap();
|
||||
assert_eq!(1, unsafe { *pixels });
|
||||
assert_eq!(2, unsafe { *(pixels.add(54)) });
|
||||
}
|
||||
|
@ -500,15 +516,15 @@ mod tests {
|
|||
let mut bmp = Bitmap::<u8>::new(8, 8).unwrap();
|
||||
bmp.pixels_mut().copy_from_slice(RAW_BMP_PIXELS);
|
||||
|
||||
assert_eq!(None, unsafe { bmp.pixels_at_mut_ptr(-1, -1) });
|
||||
assert_eq!(None, bmp.pixels_at_mut_ptr(-1, -1));
|
||||
|
||||
let offset = bmp.get_offset_to_xy(1, 1);
|
||||
let pixels = unsafe { bmp.pixels_at_mut_ptr(0, 0).unwrap() };
|
||||
let pixels = bmp.pixels_at_mut_ptr(0, 0).unwrap();
|
||||
assert_eq!(0, unsafe { *pixels });
|
||||
assert_eq!(1, unsafe { *(pixels.add(offset)) });
|
||||
assert_eq!(2, unsafe { *(pixels.add(63)) });
|
||||
|
||||
let pixels = unsafe { bmp.pixels_at_mut_ptr(1, 1).unwrap() };
|
||||
let pixels = bmp.pixels_at_mut_ptr(1, 1).unwrap();
|
||||
assert_eq!(1, unsafe { *pixels });
|
||||
assert_eq!(2, unsafe { *(pixels.add(54)) });
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use std::path::Path;
|
|||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::graphics::{from_rgb32, IndexedBitmap, Palette, PaletteError, PaletteFormat, RgbaBitmap};
|
||||
use crate::graphics::{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: &Path) -> Result<(IndexedBitmap, Palette), PcxError> {
|
||||
pub fn load_pcx_file(path: impl AsRef<Path>) -> Result<(IndexedBitmap, Palette), PcxError> {
|
||||
let f = File::open(path)?;
|
||||
let mut reader = BufReader::new(f);
|
||||
Self::load_pcx_bytes(&mut reader)
|
||||
|
@ -241,17 +241,16 @@ impl IndexedBitmap {
|
|||
writer.write_u8(0xc)?;
|
||||
|
||||
for i in 0..=255 {
|
||||
let argb = palette[i];
|
||||
let [r, g, b] = from_rgb32(argb);
|
||||
writer.write_u8(r)?;
|
||||
writer.write_u8(g)?;
|
||||
writer.write_u8(b)?;
|
||||
let color = palette[i];
|
||||
writer.write_u8(color.r())?;
|
||||
writer.write_u8(color.g())?;
|
||||
writer.write_u8(color.b())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn to_pcx_file(&self, path: &Path, palette: &Palette) -> Result<(), PcxError> {
|
||||
pub fn to_pcx_file(&self, path: impl AsRef<Path>, palette: &Palette) -> Result<(), PcxError> {
|
||||
let f = File::create(path)?;
|
||||
let mut writer = BufWriter::new(f);
|
||||
self.to_pcx_bytes(&mut writer, palette)
|
||||
|
@ -268,7 +267,7 @@ impl RgbaBitmap {
|
|||
Ok((output, palette))
|
||||
}
|
||||
|
||||
pub fn load_pcx_file(path: &Path) -> Result<(RgbaBitmap, Palette), PcxError> {
|
||||
pub fn load_pcx_file(path: impl AsRef<Path>) -> Result<(RgbaBitmap, Palette), PcxError> {
|
||||
let (temp_bitmap, palette) = IndexedBitmap::load_pcx_file(path)?;
|
||||
let output = temp_bitmap.to_rgba(&palette);
|
||||
Ok((output, palette))
|
||||
|
@ -287,7 +286,7 @@ mod tests {
|
|||
|
||||
const BASE_PATH: &str = "./test-assets/pcx/";
|
||||
|
||||
fn test_file(file: &Path) -> PathBuf {
|
||||
fn test_file(file: impl AsRef<Path>) -> PathBuf {
|
||||
PathBuf::from(BASE_PATH).join(file)
|
||||
}
|
||||
|
||||
|
@ -295,14 +294,14 @@ mod tests {
|
|||
pub fn load_and_save() -> Result<(), PcxError> {
|
||||
let tmp_dir = TempDir::new()?;
|
||||
|
||||
let ref_pixels = load_raw_indexed(test_file(Path::new("small.bin")).as_path())?;
|
||||
let ref_pixels = load_raw_indexed(test_file("small.bin"))?;
|
||||
let dp2_palette = Palette::load_from_file(
|
||||
test_assets_file(Path::new("dp2.pal")).as_path(), //
|
||||
test_assets_file("dp2.pal"), //
|
||||
PaletteFormat::Normal,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let (bmp, palette) = IndexedBitmap::load_pcx_file(test_file(Path::new("small.pcx")).as_path())?;
|
||||
let (bmp, palette) = IndexedBitmap::load_pcx_file(test_file("small.pcx"))?;
|
||||
assert_eq!(16, bmp.width());
|
||||
assert_eq!(16, bmp.height());
|
||||
assert_eq!(bmp.pixels(), ref_pixels.as_ref());
|
||||
|
@ -325,9 +324,9 @@ mod tests {
|
|||
|
||||
// first image
|
||||
|
||||
let ref_pixels = load_raw_indexed(test_file(Path::new("large_1.bin")).as_path())?;
|
||||
let ref_pixels = load_raw_indexed(test_file("large_1.bin"))?;
|
||||
|
||||
let (bmp, palette) = IndexedBitmap::load_pcx_file(test_file(Path::new("large_1.pcx")).as_path())?;
|
||||
let (bmp, palette) = IndexedBitmap::load_pcx_file(test_file("large_1.pcx"))?;
|
||||
assert_eq!(320, bmp.width());
|
||||
assert_eq!(200, bmp.height());
|
||||
assert_eq!(bmp.pixels(), ref_pixels.as_ref());
|
||||
|
@ -341,9 +340,9 @@ mod tests {
|
|||
|
||||
// second image
|
||||
|
||||
let ref_pixels = load_raw_indexed(test_file(Path::new("large_2.bin")).as_path())?;
|
||||
let ref_pixels = load_raw_indexed(test_file("large_2.bin"))?;
|
||||
|
||||
let (bmp, palette) = IndexedBitmap::load_pcx_file(test_file(Path::new("large_2.pcx")).as_path())?;
|
||||
let (bmp, palette) = IndexedBitmap::load_pcx_file(test_file("large_2.pcx"))?;
|
||||
assert_eq!(320, bmp.width());
|
||||
assert_eq!(200, bmp.height());
|
||||
assert_eq!(bmp.pixels(), ref_pixels.as_ref());
|
||||
|
|
|
@ -7,10 +7,7 @@ use std::path::Path;
|
|||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::graphics::{
|
||||
from_argb32, from_rgb32, to_argb32, to_rgb32, Bitmap, IndexedBitmap, Palette, PaletteError, PaletteFormat, Pixel,
|
||||
RgbaBitmap,
|
||||
};
|
||||
use crate::graphics::{Bitmap, IndexedBitmap, Palette, PaletteError, PaletteFormat, Pixel, RgbaBitmap, RGBA};
|
||||
use crate::utils::ReadFixedLengthByteArray;
|
||||
|
||||
const PNG_HEADER: [u8; 8] = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];
|
||||
|
@ -45,10 +42,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 {
|
||||
|
@ -56,10 +53,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)),
|
||||
}
|
||||
}
|
||||
|
@ -205,8 +202,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;
|
||||
|
@ -322,8 +319,8 @@ impl ScanlinePixelConverter<u8> for ScanlineBuffer {
|
|||
}
|
||||
}
|
||||
|
||||
impl ScanlinePixelConverter<u32> for ScanlineBuffer {
|
||||
fn read_pixel(&mut self, x: usize, palette: &Option<Palette>) -> Result<u32, PngError> {
|
||||
impl ScanlinePixelConverter<RGBA> for ScanlineBuffer {
|
||||
fn read_pixel(&mut self, x: usize, palette: &Option<Palette>) -> Result<RGBA, PngError> {
|
||||
let offset = x * self.bpp;
|
||||
match self.format {
|
||||
ColorFormat::IndexedColor => {
|
||||
|
@ -336,39 +333,37 @@ impl ScanlinePixelConverter<u32> for ScanlineBuffer {
|
|||
)))
|
||||
}
|
||||
}
|
||||
ColorFormat::RGB => {
|
||||
ColorFormat::Rgb => {
|
||||
let r = self.current[offset];
|
||||
let g = self.current[offset + 1];
|
||||
let b = self.current[offset + 2];
|
||||
Ok(to_rgb32([r, g, b]))
|
||||
Ok(RGBA::from_rgb([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(to_argb32([a, r, g, b]))
|
||||
Ok(RGBA::from_rgba([r, g, b, a]))
|
||||
}
|
||||
_ => Err(PngError::BadFile(format!("Unsupported color format for this PixelReader: {:?}", self.format))),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_pixel(&mut self, x: usize, pixel: u32) -> Result<(), PngError> {
|
||||
fn write_pixel(&mut self, x: usize, pixel: RGBA) -> Result<(), PngError> {
|
||||
let offset = x * self.bpp;
|
||||
match self.format {
|
||||
ColorFormat::RGB => {
|
||||
let [r, g, b] = from_rgb32(pixel);
|
||||
self.current[offset] = r;
|
||||
self.current[offset + 1] = g;
|
||||
self.current[offset + 2] = b;
|
||||
ColorFormat::Rgb => {
|
||||
self.current[offset] = pixel.r();
|
||||
self.current[offset + 1] = pixel.g();
|
||||
self.current[offset + 2] = pixel.b();
|
||||
Ok(())
|
||||
}
|
||||
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;
|
||||
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();
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(PngError::BadFile(format!("Unsupported color format for this PixelReader: {:?}", self.format))),
|
||||
|
@ -405,8 +400,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.")));
|
||||
}
|
||||
|
@ -565,7 +560,7 @@ impl IndexedBitmap {
|
|||
load_png_bytes(reader)
|
||||
}
|
||||
|
||||
pub fn load_png_file(path: &Path) -> Result<(IndexedBitmap, Option<Palette>), PngError> {
|
||||
pub fn load_png_file(path: impl AsRef<Path>) -> Result<(IndexedBitmap, Option<Palette>), PngError> {
|
||||
let f = File::open(path)?;
|
||||
let mut reader = BufReader::new(f);
|
||||
Self::load_png_bytes(&mut reader)
|
||||
|
@ -575,7 +570,7 @@ impl IndexedBitmap {
|
|||
write_png_bytes(writer, self, ColorFormat::IndexedColor, Some(palette))
|
||||
}
|
||||
|
||||
pub fn to_png_file(&self, path: &Path, palette: &Palette) -> Result<(), PngError> {
|
||||
pub fn to_png_file(&self, path: impl AsRef<Path>, palette: &Palette) -> Result<(), PngError> {
|
||||
let f = File::create(path)?;
|
||||
let mut writer = BufWriter::new(f);
|
||||
self.to_png_bytes(&mut writer, palette)
|
||||
|
@ -587,7 +582,7 @@ impl RgbaBitmap {
|
|||
load_png_bytes(reader)
|
||||
}
|
||||
|
||||
pub fn load_png_file(path: &Path) -> Result<(RgbaBitmap, Option<Palette>), PngError> {
|
||||
pub fn load_png_file(path: impl AsRef<Path>) -> Result<(RgbaBitmap, Option<Palette>), PngError> {
|
||||
let f = File::open(path)?;
|
||||
let mut reader = BufReader::new(f);
|
||||
Self::load_png_bytes(&mut reader)
|
||||
|
@ -598,14 +593,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: &Path, format: PngFormat) -> Result<(), PngError> {
|
||||
pub fn to_png_file(&self, path: impl AsRef<Path>, format: PngFormat) -> Result<(), PngError> {
|
||||
let f = File::create(path)?;
|
||||
let mut writer = BufWriter::new(f);
|
||||
self.to_png_bytes(&mut writer, format)
|
||||
|
@ -619,20 +614,20 @@ mod tests {
|
|||
use claim::*;
|
||||
use tempfile::TempDir;
|
||||
|
||||
use crate::tests::{load_raw_argb, load_raw_indexed};
|
||||
use crate::tests::{load_raw_indexed, load_raw_rgba};
|
||||
|
||||
use super::*;
|
||||
|
||||
const BASE_PATH: &str = "./test-assets/png/";
|
||||
|
||||
fn test_file(file: &Path) -> PathBuf {
|
||||
fn test_file(file: impl AsRef<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(Path::new("indexed_8.bin")).as_path())?;
|
||||
let (bmp, palette) = IndexedBitmap::load_png_file(test_file(Path::new("indexed_8.png")).as_path())?;
|
||||
let ref_bytes = load_raw_indexed(test_file("indexed_8.bin"))?;
|
||||
let (bmp, palette) = IndexedBitmap::load_png_file(test_file("indexed_8.png"))?;
|
||||
assert!(palette.is_some());
|
||||
assert_eq!(ref_bytes, bmp.pixels);
|
||||
Ok(())
|
||||
|
@ -640,8 +635,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
pub fn loads_indexed_256_color_to_rgba_destination() -> Result<(), PngError> {
|
||||
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())?;
|
||||
let ref_bytes = load_raw_rgba(test_file("indexed_8_rgba.bin"))?;
|
||||
let (bmp, palette) = RgbaBitmap::load_png_file(test_file("indexed_8.png"))?;
|
||||
assert!(palette.is_some());
|
||||
assert_eq!(ref_bytes, bmp.pixels);
|
||||
Ok(())
|
||||
|
@ -649,8 +644,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
pub fn loads_rgb_color() -> Result<(), PngError> {
|
||||
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())?;
|
||||
let ref_bytes = load_raw_rgba(test_file("rgb.bin"))?;
|
||||
let (bmp, palette) = RgbaBitmap::load_png_file(test_file("rgb.png"))?;
|
||||
assert!(palette.is_none());
|
||||
assert_eq!(ref_bytes, bmp.pixels);
|
||||
Ok(())
|
||||
|
@ -658,8 +653,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
pub fn loads_rgba_color() -> Result<(), PngError> {
|
||||
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())?;
|
||||
let ref_bytes = load_raw_rgba(test_file("rgba.bin"))?;
|
||||
let (bmp, palette) = RgbaBitmap::load_png_file(test_file("rgba.png"))?;
|
||||
assert!(palette.is_none());
|
||||
assert_eq!(ref_bytes, bmp.pixels);
|
||||
Ok(())
|
||||
|
@ -667,8 +662,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
pub fn loads_filter_0() -> Result<(), PngError> {
|
||||
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())?;
|
||||
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"))?;
|
||||
assert!(palette.is_none());
|
||||
assert_eq!(ref_bytes, bmp.pixels);
|
||||
Ok(())
|
||||
|
@ -676,8 +671,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
pub fn loads_filter_1() -> Result<(), PngError> {
|
||||
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())?;
|
||||
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"))?;
|
||||
assert!(palette.is_none());
|
||||
assert_eq!(ref_bytes, bmp.pixels);
|
||||
Ok(())
|
||||
|
@ -685,8 +680,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
pub fn loads_filter_2() -> Result<(), PngError> {
|
||||
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())?;
|
||||
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"))?;
|
||||
assert!(palette.is_none());
|
||||
assert_eq!(ref_bytes, bmp.pixels);
|
||||
Ok(())
|
||||
|
@ -694,8 +689,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
pub fn loads_filter_3() -> Result<(), PngError> {
|
||||
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())?;
|
||||
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"))?;
|
||||
assert!(palette.is_none());
|
||||
assert_eq!(ref_bytes, bmp.pixels);
|
||||
Ok(())
|
||||
|
@ -703,8 +698,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
pub fn loads_filter_4() -> Result<(), PngError> {
|
||||
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())?;
|
||||
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"))?;
|
||||
assert!(palette.is_none());
|
||||
assert_eq!(ref_bytes, bmp.pixels);
|
||||
Ok(())
|
||||
|
@ -712,13 +707,13 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
pub fn loads_larger_indexed_256color_images() -> Result<(), PngError> {
|
||||
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())?;
|
||||
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"))?;
|
||||
assert!(palette.is_some());
|
||||
assert_eq!(ref_bytes, bmp.pixels);
|
||||
|
||||
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())?;
|
||||
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"))?;
|
||||
assert!(palette.is_some());
|
||||
assert_eq!(ref_bytes, bmp.pixels);
|
||||
|
||||
|
@ -727,13 +722,13 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
pub fn loads_larger_rgb_images() -> Result<(), PngError> {
|
||||
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())?;
|
||||
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"))?;
|
||||
assert!(palette.is_none());
|
||||
assert_eq!(ref_bytes, bmp.pixels);
|
||||
|
||||
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())?;
|
||||
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"))?;
|
||||
assert!(palette.is_none());
|
||||
assert_eq!(ref_bytes, bmp.pixels);
|
||||
|
||||
|
@ -744,9 +739,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(Path::new("indexed_8.bin")).as_path())?;
|
||||
let ref_bytes = load_raw_indexed(test_file("indexed_8.bin"))?;
|
||||
|
||||
let (bmp, palette) = IndexedBitmap::load_png_file(test_file(Path::new("indexed_8.png")).as_path())?;
|
||||
let (bmp, palette) = IndexedBitmap::load_png_file(test_file("indexed_8.png"))?;
|
||||
assert_eq!(32, bmp.width());
|
||||
assert_eq!(32, bmp.height());
|
||||
assert_eq!(bmp.pixels, ref_bytes);
|
||||
|
@ -769,9 +764,9 @@ mod tests {
|
|||
|
||||
// first image
|
||||
|
||||
let ref_bytes = load_raw_indexed(test_file(Path::new("large_1_indexed.bin")).as_path())?;
|
||||
let ref_bytes = load_raw_indexed(test_file("large_1_indexed.bin"))?;
|
||||
|
||||
let (bmp, palette) = IndexedBitmap::load_png_file(test_file(Path::new("large_1_indexed.png")).as_path())?;
|
||||
let (bmp, palette) = IndexedBitmap::load_png_file(test_file("large_1_indexed.png"))?;
|
||||
assert_eq!(320, bmp.width());
|
||||
assert_eq!(200, bmp.height());
|
||||
assert_eq!(bmp.pixels, ref_bytes);
|
||||
|
@ -787,9 +782,9 @@ mod tests {
|
|||
|
||||
// second image
|
||||
|
||||
let ref_bytes = load_raw_indexed(test_file(Path::new("large_2_indexed.bin")).as_path())?;
|
||||
let ref_bytes = load_raw_indexed(test_file("large_2_indexed.bin"))?;
|
||||
|
||||
let (bmp, palette) = IndexedBitmap::load_png_file(test_file(Path::new("large_2_indexed.png")).as_path())?;
|
||||
let (bmp, palette) = IndexedBitmap::load_png_file(test_file("large_2_indexed.png"))?;
|
||||
assert_eq!(320, bmp.width());
|
||||
assert_eq!(200, bmp.height());
|
||||
assert_eq!(bmp.pixels, ref_bytes);
|
||||
|
@ -810,9 +805,9 @@ mod tests {
|
|||
pub fn load_and_save_rgb_color() -> Result<(), PngError> {
|
||||
let tmp_dir = TempDir::new()?;
|
||||
|
||||
let ref_bytes = load_raw_argb(test_file(Path::new("rgb.bin")).as_path())?;
|
||||
let ref_bytes = load_raw_rgba(test_file("rgb.bin"))?;
|
||||
|
||||
let (bmp, palette) = RgbaBitmap::load_png_file(test_file(Path::new("rgb.png")).as_path())?;
|
||||
let (bmp, palette) = RgbaBitmap::load_png_file(test_file("rgb.png"))?;
|
||||
assert_eq!(32, bmp.width());
|
||||
assert_eq!(32, bmp.height());
|
||||
assert_eq!(bmp.pixels, ref_bytes);
|
||||
|
@ -835,9 +830,9 @@ mod tests {
|
|||
|
||||
// first image
|
||||
|
||||
let ref_bytes = load_raw_argb(test_file(Path::new("large_1_rgba.bin")).as_path())?;
|
||||
let ref_bytes = load_raw_rgba(test_file("large_1_rgba.bin"))?;
|
||||
|
||||
let (bmp, palette) = RgbaBitmap::load_png_file(test_file(Path::new("large_1_rgb.png")).as_path())?;
|
||||
let (bmp, palette) = RgbaBitmap::load_png_file(test_file("large_1_rgb.png"))?;
|
||||
assert_eq!(320, bmp.width());
|
||||
assert_eq!(200, bmp.height());
|
||||
assert_eq!(bmp.pixels, ref_bytes);
|
||||
|
@ -853,9 +848,9 @@ mod tests {
|
|||
|
||||
// second image
|
||||
|
||||
let ref_bytes = load_raw_argb(test_file(Path::new("large_2_rgba.bin")).as_path())?;
|
||||
let ref_bytes = load_raw_rgba(test_file("large_2_rgba.bin"))?;
|
||||
|
||||
let (bmp, palette) = RgbaBitmap::load_png_file(test_file(Path::new("large_2_rgb.png")).as_path())?;
|
||||
let (bmp, palette) = RgbaBitmap::load_png_file(test_file("large_2_rgb.png"))?;
|
||||
assert_eq!(320, bmp.width());
|
||||
assert_eq!(200, bmp.height());
|
||||
assert_eq!(bmp.pixels, ref_bytes);
|
||||
|
@ -874,58 +869,43 @@ 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(Path::new("unsupported_alpha_8bit.png")).as_path()),
|
||||
RgbaBitmap::load_png_file(test_file("unsupported_greyscale_8bit.png")),
|
||||
Err(PngError::BadFile(..))
|
||||
);
|
||||
assert_matches!(
|
||||
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()),
|
||||
RgbaBitmap::load_png_file(test_file("unsupported_indexed_16col.png")),
|
||||
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(Path::new("unsupported_alpha_8bit.png")).as_path()),
|
||||
IndexedBitmap::load_png_file(test_file("unsupported_alpha_8bit.png")),
|
||||
Err(PngError::BadFile(..))
|
||||
);
|
||||
assert_matches!(
|
||||
IndexedBitmap::load_png_file(test_file(Path::new("unsupported_greyscale_8bit.png")).as_path()),
|
||||
IndexedBitmap::load_png_file(test_file("unsupported_greyscale_8bit.png")),
|
||||
Err(PngError::BadFile(..))
|
||||
);
|
||||
assert_matches!(
|
||||
IndexedBitmap::load_png_file(test_file(Path::new("unsupported_indexed_16col.png")).as_path()),
|
||||
IndexedBitmap::load_png_file(test_file("unsupported_indexed_16col.png")),
|
||||
Err(PngError::BadFile(..))
|
||||
);
|
||||
assert_matches!(
|
||||
IndexedBitmap::load_png_file(test_file(Path::new("unsupported_rgb_16bit.png")).as_path()),
|
||||
IndexedBitmap::load_png_file(test_file("unsupported_rgb_16bit.png")),
|
||||
Err(PngError::BadFile(..))
|
||||
);
|
||||
assert_matches!(
|
||||
IndexedBitmap::load_png_file(test_file(Path::new("unsupported_rgba_16bit.png")).as_path()),
|
||||
IndexedBitmap::load_png_file(test_file("unsupported_rgba_16bit.png")),
|
||||
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(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(..))
|
||||
);
|
||||
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(..)));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -31,9 +31,12 @@ impl<PixelType: Pixel> Bitmap<PixelType> {
|
|||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
/// 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.
|
||||
#[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);
|
||||
|
@ -42,8 +45,12 @@ 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. The 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.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// 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);
|
||||
|
@ -57,8 +64,12 @@ impl<PixelType: Pixel> Bitmap<PixelType> {
|
|||
self.pixels_at(x, y).map(|pixels| pixels[0])
|
||||
}
|
||||
|
||||
/// 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.
|
||||
/// 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.
|
||||
#[inline]
|
||||
pub unsafe fn get_pixel_unchecked(&self, x: i32, y: i32) -> PixelType {
|
||||
*(self.pixels_at_ptr_unchecked(x, y))
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::graphics::{
|
||||
clip_blit, per_pixel_blit, per_pixel_flipped_blit, per_pixel_rotozoom_blit, tint_argb32, BitmapAtlas,
|
||||
BlendFunction, RgbaBitmap,
|
||||
clip_blit, per_pixel_blit, per_pixel_flipped_blit, per_pixel_rotozoom_blit, BitmapAtlas, BlendFunction, RgbaBitmap,
|
||||
RGBA,
|
||||
};
|
||||
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(u32),
|
||||
SolidTinted(RGBA),
|
||||
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: u32,
|
||||
tint_color: RGBA,
|
||||
},
|
||||
SolidFlippedBlended {
|
||||
horizontal_flip: bool,
|
||||
|
@ -27,30 +27,30 @@ pub enum RgbaBlitMethod {
|
|||
blend: BlendFunction,
|
||||
},
|
||||
/// Transparent blit, the specified source color pixels are skipped.
|
||||
Transparent(u32),
|
||||
Transparent(RGBA),
|
||||
TransparentTinted {
|
||||
transparent_color: u32,
|
||||
tint_color: u32,
|
||||
transparent_color: RGBA,
|
||||
tint_color: RGBA,
|
||||
},
|
||||
TransparentBlended {
|
||||
transparent_color: u32,
|
||||
transparent_color: RGBA,
|
||||
blend: BlendFunction,
|
||||
},
|
||||
/// Same as [RgbaBlitMethod::Transparent] but the drawn image can also be flipped horizontally
|
||||
/// and/or vertically.
|
||||
TransparentFlipped {
|
||||
transparent_color: u32,
|
||||
transparent_color: RGBA,
|
||||
horizontal_flip: bool,
|
||||
vertical_flip: bool,
|
||||
},
|
||||
TransparentFlippedTinted {
|
||||
transparent_color: u32,
|
||||
transparent_color: RGBA,
|
||||
horizontal_flip: bool,
|
||||
vertical_flip: bool,
|
||||
tint_color: u32,
|
||||
tint_color: RGBA,
|
||||
},
|
||||
TransparentFlippedBlended {
|
||||
transparent_color: u32,
|
||||
transparent_color: RGBA,
|
||||
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: u32,
|
||||
draw_color: u32,
|
||||
transparent_color: RGBA,
|
||||
draw_color: RGBA,
|
||||
},
|
||||
/// Combination of [RgbaBlitMethod::TransparentFlipped] and [RgbaBlitMethod::TransparentSingle].
|
||||
TransparentFlippedSingle {
|
||||
transparent_color: u32,
|
||||
transparent_color: RGBA,
|
||||
horizontal_flip: bool,
|
||||
vertical_flip: bool,
|
||||
draw_color: u32,
|
||||
draw_color: RGBA,
|
||||
},
|
||||
/// 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: u32,
|
||||
tint_color: RGBA,
|
||||
},
|
||||
RotoZoomBlended {
|
||||
angle: f32,
|
||||
|
@ -92,32 +92,36 @@ pub enum RgbaBlitMethod {
|
|||
angle: f32,
|
||||
scale_x: f32,
|
||||
scale_y: f32,
|
||||
transparent_color: u32,
|
||||
transparent_color: RGBA,
|
||||
},
|
||||
RotoZoomTransparentTinted {
|
||||
angle: f32,
|
||||
scale_x: f32,
|
||||
scale_y: f32,
|
||||
transparent_color: u32,
|
||||
tint_color: u32,
|
||||
transparent_color: RGBA,
|
||||
tint_color: RGBA,
|
||||
},
|
||||
RotoZoomTransparentBlended {
|
||||
angle: f32,
|
||||
scale_x: f32,
|
||||
scale_y: f32,
|
||||
transparent_color: u32,
|
||||
transparent_color: RGBA,
|
||||
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: u32,
|
||||
tint_color: RGBA,
|
||||
) {
|
||||
per_pixel_blit(
|
||||
self, //
|
||||
|
@ -126,11 +130,15 @@ impl RgbaBitmap {
|
|||
dest_x,
|
||||
dest_y,
|
||||
|src_pixels, dest_pixels| {
|
||||
*dest_pixels = tint_argb32(*src_pixels, tint_color);
|
||||
*dest_pixels = (*src_pixels).tint(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,
|
||||
|
@ -146,11 +154,15 @@ impl RgbaBitmap {
|
|||
dest_x,
|
||||
dest_y,
|
||||
|src_pixels, dest_pixels| {
|
||||
*dest_pixels = blend.blend_1u32(*src_pixels, *dest_pixels);
|
||||
*dest_pixels = blend.blend(*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,
|
||||
|
@ -170,11 +182,15 @@ impl RgbaBitmap {
|
|||
horizontal_flip,
|
||||
vertical_flip,
|
||||
|src_pixels, dest_pixels| {
|
||||
*dest_pixels = blend.blend_1u32(*src_pixels, *dest_pixels);
|
||||
*dest_pixels = blend.blend(*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,
|
||||
|
@ -183,7 +199,7 @@ impl RgbaBitmap {
|
|||
dest_y: i32,
|
||||
horizontal_flip: bool,
|
||||
vertical_flip: bool,
|
||||
tint_color: u32,
|
||||
tint_color: RGBA,
|
||||
) {
|
||||
per_pixel_flipped_blit(
|
||||
self, //
|
||||
|
@ -194,19 +210,23 @@ impl RgbaBitmap {
|
|||
horizontal_flip,
|
||||
vertical_flip,
|
||||
|src_pixels, dest_pixels| {
|
||||
*dest_pixels = tint_argb32(*src_pixels, tint_color);
|
||||
*dest_pixels = (*src_pixels).tint(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: u32,
|
||||
tint_color: u32,
|
||||
transparent_color: RGBA,
|
||||
tint_color: RGBA,
|
||||
) {
|
||||
per_pixel_blit(
|
||||
self, //
|
||||
|
@ -216,19 +236,23 @@ impl RgbaBitmap {
|
|||
dest_y,
|
||||
|src_pixels, dest_pixels| {
|
||||
if *src_pixels != transparent_color {
|
||||
*dest_pixels = tint_argb32(*src_pixels, tint_color);
|
||||
*dest_pixels = (*src_pixels).tint(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: u32,
|
||||
transparent_color: RGBA,
|
||||
blend: BlendFunction,
|
||||
) {
|
||||
per_pixel_blit(
|
||||
|
@ -239,22 +263,26 @@ impl RgbaBitmap {
|
|||
dest_y,
|
||||
|src_pixels, dest_pixels| {
|
||||
if *src_pixels != transparent_color {
|
||||
*dest_pixels = blend.blend_1u32(*src_pixels, *dest_pixels);
|
||||
*dest_pixels = blend.blend(*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: u32,
|
||||
transparent_color: RGBA,
|
||||
horizontal_flip: bool,
|
||||
vertical_flip: bool,
|
||||
tint_color: u32,
|
||||
tint_color: RGBA,
|
||||
) {
|
||||
per_pixel_flipped_blit(
|
||||
self, //
|
||||
|
@ -266,19 +294,23 @@ impl RgbaBitmap {
|
|||
vertical_flip,
|
||||
|src_pixels, dest_pixels| {
|
||||
if *src_pixels != transparent_color {
|
||||
*dest_pixels = tint_argb32(*src_pixels, tint_color);
|
||||
*dest_pixels = (*src_pixels).tint(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: u32,
|
||||
transparent_color: RGBA,
|
||||
horizontal_flip: bool,
|
||||
vertical_flip: bool,
|
||||
blend: BlendFunction,
|
||||
|
@ -293,12 +325,16 @@ impl RgbaBitmap {
|
|||
vertical_flip,
|
||||
|src_pixels, dest_pixels| {
|
||||
if *src_pixels != transparent_color {
|
||||
*dest_pixels = blend.blend_1u32(*src_pixels, *dest_pixels);
|
||||
*dest_pixels = blend.blend(*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,
|
||||
|
@ -308,7 +344,7 @@ impl RgbaBitmap {
|
|||
angle: f32,
|
||||
scale_x: f32,
|
||||
scale_y: f32,
|
||||
tint_color: u32,
|
||||
tint_color: RGBA,
|
||||
) {
|
||||
per_pixel_rotozoom_blit(
|
||||
self, //
|
||||
|
@ -320,11 +356,15 @@ impl RgbaBitmap {
|
|||
scale_x,
|
||||
scale_y,
|
||||
|src_pixel, dest_bitmap, draw_x, draw_y| {
|
||||
dest_bitmap.set_pixel(draw_x, draw_y, tint_argb32(src_pixel, tint_color));
|
||||
dest_bitmap.set_pixel(draw_x, draw_y, src_pixel.tint(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,
|
||||
|
@ -347,12 +387,16 @@ 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_1u32(src_pixel, dest_pixel))
|
||||
dest_bitmap.set_pixel(draw_x, draw_y, blend.blend(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,
|
||||
|
@ -362,8 +406,8 @@ impl RgbaBitmap {
|
|||
angle: f32,
|
||||
scale_x: f32,
|
||||
scale_y: f32,
|
||||
transparent_color: u32,
|
||||
tint_color: u32,
|
||||
transparent_color: RGBA,
|
||||
tint_color: RGBA,
|
||||
) {
|
||||
per_pixel_rotozoom_blit(
|
||||
self, //
|
||||
|
@ -376,12 +420,16 @@ 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, tint_argb32(src_pixel, tint_color));
|
||||
dest_bitmap.set_pixel(draw_x, draw_y, src_pixel.tint(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,
|
||||
|
@ -391,7 +439,7 @@ impl RgbaBitmap {
|
|||
angle: f32,
|
||||
scale_x: f32,
|
||||
scale_y: f32,
|
||||
transparent_color: u32,
|
||||
transparent_color: RGBA,
|
||||
blend: BlendFunction,
|
||||
) {
|
||||
per_pixel_rotozoom_blit(
|
||||
|
@ -406,7 +454,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_1u32(src_pixel, dest_pixel))
|
||||
dest_bitmap.set_pixel(draw_x, draw_y, blend.blend(src_pixel, dest_pixel))
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -479,6 +527,10 @@ 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(
|
||||
|
@ -561,12 +613,20 @@ 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,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use byteorder::ReadBytesExt;
|
||||
use std::path::Path;
|
||||
|
||||
use crate::graphics::{to_argb32, to_rgb32, Bitmap, BitmapError, Palette};
|
||||
use crate::graphics::{Bitmap, BitmapError, Palette, RGBA};
|
||||
|
||||
mod blit;
|
||||
mod primitives;
|
||||
|
@ -11,7 +11,7 @@ pub use blit::*;
|
|||
pub use primitives::*;
|
||||
pub use triangles::*;
|
||||
|
||||
pub type RgbaBitmap = Bitmap<u32>;
|
||||
pub type RgbaBitmap = Bitmap<RGBA>;
|
||||
|
||||
#[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, to_rgb32([0, 0, 0]))
|
||||
Self::internal_new(width, height, RGBA::from_rgb([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, 0)?;
|
||||
let mut bitmap = Self::internal_new(width, height, RGBA::from_rgb([0, 0, 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()?;
|
||||
to_argb32([a, r, g, b])
|
||||
RGBA::from_rgba([r, g, b, a])
|
||||
}
|
||||
RgbaPixelFormat::ARGB => {
|
||||
let a = reader.read_u8()?;
|
||||
let r = reader.read_u8()?;
|
||||
let g = reader.read_u8()?;
|
||||
let b = reader.read_u8()?;
|
||||
to_argb32([a, r, g, b])
|
||||
RGBA::from_rgba([r, g, b, a])
|
||||
}
|
||||
};
|
||||
}
|
||||
Ok(bitmap)
|
||||
}
|
||||
|
||||
pub fn load_file(path: &Path) -> Result<(Self, Option<Palette>), BitmapError> {
|
||||
if let Some(extension) = path.extension() {
|
||||
pub fn load_file(path: impl AsRef<Path>) -> Result<(Self, Option<Palette>), BitmapError> {
|
||||
if let Some(extension) = path.as_ref().extension() {
|
||||
let extension = extension.to_ascii_lowercase();
|
||||
match extension.to_str() {
|
||||
Some("png") => Ok(Self::load_png_file(path)?),
|
||||
|
|
|
@ -1,85 +1,93 @@
|
|||
use crate::graphics::{BlendFunction, RgbaBitmap};
|
||||
use crate::graphics::{BlendFunction, RgbaBitmap, RGBA};
|
||||
|
||||
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: u32, blend: BlendFunction) {
|
||||
pub fn set_blended_pixel(&mut self, x: i32, y: i32, color: RGBA, blend: BlendFunction) {
|
||||
self.set_custom_pixel(
|
||||
x, //
|
||||
y,
|
||||
|dest_color| blend.blend_1u32(color, dest_color),
|
||||
|dest_color| blend.blend(color, dest_color),
|
||||
);
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// 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
|
||||
/// bounds of the bitmap.
|
||||
#[inline]
|
||||
pub unsafe fn set_blended_pixel_unchecked(&mut self, x: i32, y: i32, color: u32, blend: BlendFunction) {
|
||||
pub unsafe fn set_blended_pixel_unchecked(&mut self, x: i32, y: i32, color: RGBA, blend: BlendFunction) {
|
||||
self.set_custom_pixel_unchecked(
|
||||
x, //
|
||||
y,
|
||||
|dest_color| blend.blend_1u32(color, dest_color),
|
||||
|dest_color| blend.blend(color, dest_color),
|
||||
);
|
||||
}
|
||||
|
||||
/// Draws a line from x1,y1 to x2,y2 by blending the drawn pixels using the given blend function.
|
||||
pub fn blended_line(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: u32, blend: BlendFunction) {
|
||||
#[inline]
|
||||
pub fn blended_line(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: RGBA, blend: BlendFunction) {
|
||||
self.line_custom(
|
||||
x1, //
|
||||
y1,
|
||||
x2,
|
||||
y2,
|
||||
|dest_color| blend.blend_1u32(color, dest_color),
|
||||
|dest_color| blend.blend(color, dest_color),
|
||||
);
|
||||
}
|
||||
|
||||
/// Draws a horizontal line from x1,y to x2,y by blending the drawn pixels using the given
|
||||
/// blend function.
|
||||
pub fn blended_horiz_line(&mut self, x1: i32, x2: i32, y: i32, color: u32, blend: BlendFunction) {
|
||||
#[inline]
|
||||
pub fn blended_horiz_line(&mut self, x1: i32, x2: i32, y: i32, color: RGBA, blend: BlendFunction) {
|
||||
self.horiz_line_custom(
|
||||
x1, //
|
||||
x2,
|
||||
y,
|
||||
|dest_color| blend.blend_1u32(color, dest_color),
|
||||
|dest_color| blend.blend(color, dest_color),
|
||||
);
|
||||
}
|
||||
|
||||
/// Draws a vertical line from x,y1 to x,y2 by blending the drawn pixels using the given blend
|
||||
/// function.
|
||||
pub fn blended_vert_line(&mut self, x: i32, y1: i32, y2: i32, color: u32, blend: BlendFunction) {
|
||||
#[inline]
|
||||
pub fn blended_vert_line(&mut self, x: i32, y1: i32, y2: i32, color: RGBA, blend: BlendFunction) {
|
||||
self.vert_line_custom(
|
||||
x, //
|
||||
y1,
|
||||
y2,
|
||||
|dest_color| blend.blend_1u32(color, dest_color),
|
||||
|dest_color| blend.blend(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.
|
||||
pub fn blended_rect(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: u32, blend: BlendFunction) {
|
||||
#[inline]
|
||||
pub fn blended_rect(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: RGBA, blend: BlendFunction) {
|
||||
self.rect_custom(
|
||||
x1, //
|
||||
y1,
|
||||
x2,
|
||||
y2,
|
||||
|dest_color| blend.blend_1u32(color, dest_color),
|
||||
|dest_color| blend.blend(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.
|
||||
pub fn blended_filled_rect(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: u32, blend: BlendFunction) {
|
||||
#[inline]
|
||||
pub fn blended_filled_rect(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: RGBA, blend: BlendFunction) {
|
||||
self.filled_rect_custom(
|
||||
x1, //
|
||||
y1,
|
||||
x2,
|
||||
y2,
|
||||
|dest_color| blend.blend_1u32(color, dest_color),
|
||||
|dest_color| blend.blend(color, dest_color),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,29 +1,27 @@
|
|||
use std::simd;
|
||||
use std::simd::prelude::{SimdFloat, SimdUint};
|
||||
|
||||
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::graphics::{edge_function, per_pixel_triangle_2d, BlendFunction, RgbaBitmap, RGBA};
|
||||
use crate::math::Vector2;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum RgbaTriangle2d<'a> {
|
||||
Solid {
|
||||
position: [Vector2; 3], //
|
||||
color: u32,
|
||||
color: RGBA,
|
||||
},
|
||||
SolidBlended {
|
||||
position: [Vector2; 3], //
|
||||
color: u32,
|
||||
color: RGBA,
|
||||
blend: BlendFunction,
|
||||
},
|
||||
SolidMultiColor {
|
||||
position: [Vector2; 3], //
|
||||
color: [u32; 3],
|
||||
color: [RGBA; 3],
|
||||
},
|
||||
SolidMultiColorBlended {
|
||||
position: [Vector2; 3], //
|
||||
color: [u32; 3],
|
||||
color: [RGBA; 3],
|
||||
blend: BlendFunction,
|
||||
},
|
||||
SolidTextured {
|
||||
|
@ -34,26 +32,26 @@ pub enum RgbaTriangle2d<'a> {
|
|||
SolidTexturedColored {
|
||||
position: [Vector2; 3], //
|
||||
texcoord: [Vector2; 3],
|
||||
color: u32,
|
||||
color: RGBA,
|
||||
bitmap: &'a RgbaBitmap,
|
||||
},
|
||||
SolidTexturedColoredBlended {
|
||||
position: [Vector2; 3], //
|
||||
texcoord: [Vector2; 3],
|
||||
color: u32,
|
||||
color: RGBA,
|
||||
bitmap: &'a RgbaBitmap,
|
||||
blend: BlendFunction,
|
||||
},
|
||||
SolidTexturedMultiColored {
|
||||
position: [Vector2; 3], //
|
||||
texcoord: [Vector2; 3],
|
||||
color: [u32; 3],
|
||||
color: [RGBA; 3],
|
||||
bitmap: &'a RgbaBitmap,
|
||||
},
|
||||
SolidTexturedMultiColoredBlended {
|
||||
position: [Vector2; 3], //
|
||||
texcoord: [Vector2; 3],
|
||||
color: [u32; 3],
|
||||
color: [RGBA; 3],
|
||||
bitmap: &'a RgbaBitmap,
|
||||
blend: BlendFunction,
|
||||
},
|
||||
|
@ -61,7 +59,7 @@ pub enum RgbaTriangle2d<'a> {
|
|||
position: [Vector2; 3], //
|
||||
texcoord: [Vector2; 3],
|
||||
bitmap: &'a RgbaBitmap,
|
||||
tint: u32,
|
||||
tint: RGBA,
|
||||
},
|
||||
SolidTexturedBlended {
|
||||
position: [Vector2; 3], //
|
||||
|
@ -72,7 +70,7 @@ pub enum RgbaTriangle2d<'a> {
|
|||
}
|
||||
|
||||
impl RgbaBitmap {
|
||||
pub fn solid_triangle_2d(&mut self, positions: &[Vector2; 3], color: u32) {
|
||||
pub fn solid_triangle_2d(&mut self, positions: &[Vector2; 3], color: RGBA) {
|
||||
per_pixel_triangle_2d(
|
||||
self, //
|
||||
positions[0],
|
||||
|
@ -82,24 +80,21 @@ impl RgbaBitmap {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn solid_blended_triangle_2d(&mut self, positions: &[Vector2; 3], color: u32, blend: BlendFunction) {
|
||||
let color = from_argb32_simd(color);
|
||||
pub fn solid_blended_triangle_2d(&mut self, positions: &[Vector2; 3], color: RGBA, blend: BlendFunction) {
|
||||
per_pixel_triangle_2d(
|
||||
self, //
|
||||
positions[0],
|
||||
positions[1],
|
||||
positions[2],
|
||||
|dest_pixels, _w0, _w1, _w2| {
|
||||
*dest_pixels = to_argb32_simd(blend.blend_simd(color, from_argb32_simd(*dest_pixels)))
|
||||
},
|
||||
|dest_pixels, _w0, _w1, _w2| *dest_pixels = blend.blend(color, *dest_pixels),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn solid_multicolor_triangle_2d(&mut self, positions: &[Vector2; 3], colors: &[u32; 3]) {
|
||||
pub fn solid_multicolor_triangle_2d(&mut self, positions: &[Vector2; 3], colors: &[RGBA; 3]) {
|
||||
let area = simd::f32x4::splat(edge_function(positions[0], positions[1], positions[2]));
|
||||
let color1 = from_rgb32_simd(colors[0]).cast();
|
||||
let color2 = from_rgb32_simd(colors[1]).cast();
|
||||
let color3 = from_rgb32_simd(colors[2]).cast();
|
||||
let color1 = colors[0].0.cast();
|
||||
let color2 = colors[1].0.cast();
|
||||
let color3 = colors[2].0.cast();
|
||||
per_pixel_triangle_2d(
|
||||
self, //
|
||||
positions[0],
|
||||
|
@ -111,7 +106,7 @@ impl RgbaBitmap {
|
|||
+ simd::f32x4::splat(w2) * color3)
|
||||
/ area)
|
||||
.cast();
|
||||
*dest_pixels = to_rgb32_simd(color)
|
||||
*dest_pixels = RGBA(color)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -119,13 +114,13 @@ impl RgbaBitmap {
|
|||
pub fn solid_multicolor_blended_triangle_2d(
|
||||
&mut self,
|
||||
positions: &[Vector2; 3],
|
||||
colors: &[u32; 3],
|
||||
colors: &[RGBA; 3],
|
||||
blend: BlendFunction,
|
||||
) {
|
||||
let area = simd::f32x4::splat(edge_function(positions[0], positions[1], positions[2]));
|
||||
let color1 = from_argb32_simd(colors[0]).cast();
|
||||
let color2 = from_argb32_simd(colors[1]).cast();
|
||||
let color3 = from_argb32_simd(colors[2]).cast();
|
||||
let color1 = colors[0].0.cast();
|
||||
let color2 = colors[1].0.cast();
|
||||
let color3 = colors[2].0.cast();
|
||||
per_pixel_triangle_2d(
|
||||
self, //
|
||||
positions[0],
|
||||
|
@ -137,7 +132,7 @@ impl RgbaBitmap {
|
|||
+ simd::f32x4::splat(w2) * color3)
|
||||
/ area)
|
||||
.cast();
|
||||
*dest_pixels = to_argb32_simd(blend.blend_simd(color, from_argb32_simd(*dest_pixels)))
|
||||
*dest_pixels = blend.blend(RGBA(color), *dest_pixels)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -166,11 +161,10 @@ impl RgbaBitmap {
|
|||
&mut self,
|
||||
positions: &[Vector2; 3],
|
||||
texcoords: &[Vector2; 3],
|
||||
color: u32,
|
||||
color: RGBA,
|
||||
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]);
|
||||
|
@ -184,8 +178,8 @@ impl RgbaBitmap {
|
|||
+ simd::f32x2::splat(w1) * texcoord2
|
||||
+ simd::f32x2::splat(w2) * texcoord3)
|
||||
/ area;
|
||||
let texel = from_argb32_simd(bitmap.sample_at(texcoord[0], texcoord[1]));
|
||||
*dest_pixels = to_argb32_simd(multiply_argb_simd(texel, color))
|
||||
let texel = bitmap.sample_at(texcoord[0], texcoord[1]);
|
||||
*dest_pixels = texel * color
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -194,12 +188,11 @@ impl RgbaBitmap {
|
|||
&mut self,
|
||||
positions: &[Vector2; 3],
|
||||
texcoords: &[Vector2; 3],
|
||||
color: u32,
|
||||
color: RGBA,
|
||||
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]);
|
||||
|
@ -213,9 +206,9 @@ impl RgbaBitmap {
|
|||
+ simd::f32x2::splat(w1) * texcoord2
|
||||
+ simd::f32x2::splat(w2) * texcoord3)
|
||||
/ area;
|
||||
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)))
|
||||
let texel = bitmap.sample_at(texcoord[0], texcoord[1]);
|
||||
let src = texel * color;
|
||||
*dest_pixels = blend.blend(src, *dest_pixels)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -224,13 +217,13 @@ impl RgbaBitmap {
|
|||
&mut self,
|
||||
positions: &[Vector2; 3],
|
||||
texcoords: &[Vector2; 3],
|
||||
colors: &[u32; 3],
|
||||
colors: &[RGBA; 3],
|
||||
bitmap: &Self,
|
||||
) {
|
||||
let area = simd::f32x4::splat(edge_function(positions[0], positions[1], positions[2]));
|
||||
let color1 = from_rgb32_simd(colors[0]).cast();
|
||||
let color2 = from_rgb32_simd(colors[1]).cast();
|
||||
let color3 = from_rgb32_simd(colors[2]).cast();
|
||||
let color1 = colors[0].0.cast();
|
||||
let color2 = colors[1].0.cast();
|
||||
let color3 = colors[2].0.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 ...
|
||||
|
@ -248,8 +241,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 = from_argb32_simd(bitmap.sample_at(texcoord[0], texcoord[1]));
|
||||
*dest_pixels = to_rgb32_simd(multiply_argb_simd(texel, color))
|
||||
let texel = bitmap.sample_at(texcoord[0], texcoord[1]);
|
||||
*dest_pixels = texel * RGBA(color)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -258,14 +251,14 @@ impl RgbaBitmap {
|
|||
&mut self,
|
||||
positions: &[Vector2; 3],
|
||||
texcoords: &[Vector2; 3],
|
||||
colors: &[u32; 3],
|
||||
colors: &[RGBA; 3],
|
||||
bitmap: &Self,
|
||||
blend: BlendFunction,
|
||||
) {
|
||||
let area = simd::f32x4::splat(edge_function(positions[0], positions[1], positions[2]));
|
||||
let color1 = from_argb32_simd(colors[0]).cast();
|
||||
let color2 = from_argb32_simd(colors[1]).cast();
|
||||
let color3 = from_argb32_simd(colors[2]).cast();
|
||||
let color1 = colors[0].0.cast();
|
||||
let color2 = colors[1].0.cast();
|
||||
let color3 = colors[2].0.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 ...
|
||||
|
@ -283,10 +276,9 @@ 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 = 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))
|
||||
let texel = bitmap.sample_at(texcoord[0], texcoord[1]);
|
||||
let src = texel * RGBA(color);
|
||||
*dest_pixels = blend.blend(src, *dest_pixels)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -296,10 +288,9 @@ impl RgbaBitmap {
|
|||
positions: &[Vector2; 3],
|
||||
texcoords: &[Vector2; 3],
|
||||
bitmap: &Self,
|
||||
tint: u32,
|
||||
tint: RGBA,
|
||||
) {
|
||||
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]);
|
||||
|
@ -313,8 +304,8 @@ impl RgbaBitmap {
|
|||
+ simd::f32x2::splat(w1) * texcoord2
|
||||
+ simd::f32x2::splat(w2) * texcoord3)
|
||||
/ area;
|
||||
let texel = from_argb32_simd(bitmap.sample_at(texcoord[0], texcoord[1]));
|
||||
*dest_pixels = to_argb32_simd(tint_argb_simd(texel, tint));
|
||||
let texel = bitmap.sample_at(texcoord[0], texcoord[1]);
|
||||
*dest_pixels = texel.tint(tint);
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -340,8 +331,8 @@ impl RgbaBitmap {
|
|||
+ simd::f32x2::splat(w1) * texcoord2
|
||||
+ simd::f32x2::splat(w2) * texcoord3)
|
||||
/ area;
|
||||
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)));
|
||||
let texel = bitmap.sample_at(texcoord[0], texcoord[1]);
|
||||
*dest_pixels = blend.blend(texel, *dest_pixels);
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,14 +1,26 @@
|
|||
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;
|
||||
use crate::graphics::{GeneralBitmap, GeneralBlitMethod};
|
||||
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)]
|
||||
|
@ -34,7 +46,31 @@ 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);
|
||||
}
|
||||
|
@ -44,6 +80,9 @@ 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);
|
||||
}
|
||||
|
@ -70,6 +109,10 @@ 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(
|
||||
|
@ -114,10 +157,31 @@ 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>
|
||||
|
@ -132,6 +196,70 @@ 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::*;
|
||||
|
@ -170,6 +298,16 @@ 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]);
|
||||
|
@ -187,4 +325,26 @@ 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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use std::path::Path;
|
|||
use byteorder::{ReadBytesExt, WriteBytesExt};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::graphics::{from_rgb32, luminance, Palette};
|
||||
use crate::graphics::Palette;
|
||||
use crate::math::lerp;
|
||||
use crate::utils::ReadFixedLengthByteArray;
|
||||
|
||||
|
@ -76,8 +76,7 @@ impl BlendMap {
|
|||
|
||||
let mut blend_map = Self::new(source_color, source_color);
|
||||
for idx in 0..=255 {
|
||||
let rgb = from_rgb32(palette[idx]);
|
||||
let lit = (luminance(rgb) * 255.0) as u8;
|
||||
let lit = (palette[idx].luminance() * 255.0) as u8;
|
||||
blend_map
|
||||
.set_mapping(
|
||||
source_color,
|
||||
|
@ -105,11 +104,9 @@ impl BlendMap {
|
|||
|
||||
let mut blend_map = BlendMap::new(0, 255);
|
||||
for source_color in 0..=255 {
|
||||
let source_rgb = from_rgb32(palette[source_color]);
|
||||
let source_luminance = luminance(source_rgb);
|
||||
let source_luminance = palette[source_color].luminance();
|
||||
for dest_color in 0..=255 {
|
||||
let dest_rgb = from_rgb32(palette[dest_color]);
|
||||
let destination_luminance = luminance(dest_rgb);
|
||||
let destination_luminance = palette[dest_color].luminance();
|
||||
let weight = (f(source_luminance, destination_luminance) * 255.0) as u8;
|
||||
blend_map
|
||||
.set_mapping(
|
||||
|
@ -135,10 +132,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] = from_rgb32(palette[source]);
|
||||
let [_, source_r, source_g, source_b] = palette[source].to_array();
|
||||
let mapping = blend_map.get_mapping_mut(source).unwrap();
|
||||
for dest in 0..=255 {
|
||||
let [dest_r, dest_g, dest_b] = from_rgb32(palette[dest]);
|
||||
let [_, dest_r, dest_g, dest_b] = palette[dest].to_array();
|
||||
|
||||
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;
|
||||
|
@ -242,7 +239,7 @@ impl BlendMap {
|
|||
self.get_mapping(source_color).map(|mapping| mapping[dest_color as usize])
|
||||
}
|
||||
|
||||
pub fn load_from_file(path: &Path) -> Result<Self, BlendMapError> {
|
||||
pub fn load_from_file(path: impl AsRef<Path>) -> Result<Self, BlendMapError> {
|
||||
let f = File::open(path)?;
|
||||
let mut reader = BufReader::new(f);
|
||||
Self::load_from_bytes(&mut reader)
|
||||
|
@ -271,7 +268,7 @@ impl BlendMap {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn to_file(&self, path: &Path) -> Result<(), BlendMapError> {
|
||||
pub fn to_file(&self, path: impl AsRef<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
|
@ -1,5 +1,6 @@
|
|||
use std::fs::File;
|
||||
use std::io::{BufReader, BufWriter, Cursor};
|
||||
use std::ops::{Index, IndexMut};
|
||||
use std::path::Path;
|
||||
|
||||
use byteorder::{ReadBytesExt, WriteBytesExt};
|
||||
|
@ -21,6 +22,15 @@ 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)]
|
||||
|
@ -47,12 +57,38 @@ pub trait Font {
|
|||
PixelType: Pixel;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
#[derive(Debug, Copy, 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 {
|
||||
|
@ -94,6 +130,42 @@ 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]>,
|
||||
|
@ -116,7 +188,28 @@ impl BitmaskFont {
|
|||
BitmaskFont::load_from_bytes(&mut Cursor::new(VGA_FONT_BYTES))
|
||||
}
|
||||
|
||||
pub fn load_from_file(path: &Path) -> Result<BitmaskFont, FontError> {
|
||||
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> {
|
||||
let f = File::open(path)?;
|
||||
let mut reader = BufReader::new(f);
|
||||
|
||||
|
@ -139,26 +232,17 @@ impl BitmaskFont {
|
|||
}
|
||||
|
||||
// read character widths (used for rendering)
|
||||
for i in 0..NUM_CHARS {
|
||||
characters[i].bounds.width = reader.read_u8()? as u32;
|
||||
for character in characters.iter_mut().take(NUM_CHARS) {
|
||||
character.bounds.width = reader.read_u8()? as u32;
|
||||
}
|
||||
|
||||
// read global font height (used for rendering)
|
||||
let line_height = reader.read_u8()?;
|
||||
for i in 0..NUM_CHARS {
|
||||
characters[i].bounds.height = line_height as u32;
|
||||
}
|
||||
|
||||
let space_width = characters[' ' as usize].bounds.width as u8;
|
||||
|
||||
Ok(BitmaskFont {
|
||||
characters: characters.into_boxed_slice(), //
|
||||
line_height,
|
||||
space_width,
|
||||
})
|
||||
Self::new(&characters, line_height as usize)
|
||||
}
|
||||
|
||||
pub fn to_file(&self, path: &Path) -> Result<(), FontError> {
|
||||
pub fn to_file(&self, path: impl AsRef<Path>) -> Result<(), FontError> {
|
||||
let f = File::create(path)?;
|
||||
let mut writer = BufWriter::new(f);
|
||||
self.to_bytes(&mut writer)
|
||||
|
@ -243,13 +327,13 @@ mod tests {
|
|||
|
||||
const BASE_PATH: &str = "./test-assets/font/";
|
||||
|
||||
fn test_file(file: &Path) -> PathBuf {
|
||||
fn test_file(file: impl AsRef<Path>) -> PathBuf {
|
||||
PathBuf::from(BASE_PATH).join(file)
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn load_font() -> Result<(), FontError> {
|
||||
let font = BitmaskFont::load_from_file(test_file(Path::new("vga.fnt")).as_path())?;
|
||||
let font = BitmaskFont::load_from_file(test_file("vga.fnt"))?;
|
||||
assert_eq!(256, font.characters.len());
|
||||
assert_eq!(CHAR_FIXED_WIDTH as u8, font.space_width);
|
||||
for character in font.characters.iter() {
|
||||
|
@ -263,7 +347,7 @@ mod tests {
|
|||
#[test]
|
||||
pub fn measure_text() -> Result<(), FontError> {
|
||||
{
|
||||
let font = BitmaskFont::load_from_file(test_file(Path::new("vga.fnt")).as_path())?;
|
||||
let font = BitmaskFont::load_from_file(test_file("vga.fnt"))?;
|
||||
|
||||
assert_eq!((40, 8), font.measure("Hello", FontRenderOpts::<u8>::None));
|
||||
assert_eq!((40, 16), font.measure("Hello\nthere", FontRenderOpts::<u8>::None));
|
||||
|
@ -277,7 +361,7 @@ mod tests {
|
|||
}
|
||||
|
||||
{
|
||||
let font = BitmaskFont::load_from_file(test_file(Path::new("small.fnt")).as_path())?;
|
||||
let font = BitmaskFont::load_from_file(test_file("small.fnt"))?;
|
||||
|
||||
assert_eq!((22, 7), font.measure("Hello", FontRenderOpts::<u8>::None));
|
||||
assert_eq!((24, 14), font.measure("Hello\nthere", FontRenderOpts::<u8>::None));
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
use num_traits::{PrimInt, Unsigned};
|
||||
use std::fmt::Display;
|
||||
|
||||
mod bitmap;
|
||||
|
@ -16,5 +15,5 @@ pub use font::*;
|
|||
pub use palette::*;
|
||||
|
||||
/// Common trait to represent single pixel/colour values.
|
||||
pub trait Pixel: PrimInt + Unsigned + Default + Display {}
|
||||
impl<T> Pixel for T where T: PrimInt + Unsigned + Default + Display {}
|
||||
pub trait Pixel: Default + Display + Eq + Copy + Clone {}
|
||||
impl<T> Pixel for T where T: Default + Display + Eq + Copy + Clone {}
|
||||
|
|
|
@ -7,7 +7,7 @@ use std::path::Path;
|
|||
use byteorder::{ReadBytesExt, WriteBytesExt};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::graphics::{from_rgb32, lerp_rgb32, to_argb32, to_rgb32, IndexedBitmap};
|
||||
use crate::graphics::{IndexedBitmap, RGBA};
|
||||
use crate::utils::abs_diff;
|
||||
|
||||
const NUM_COLORS: usize = 256;
|
||||
|
@ -29,67 +29,63 @@ fn to_6bit(value: u8) -> u8 {
|
|||
}
|
||||
|
||||
// vga bios (0-63) format
|
||||
fn read_palette_6bit<T: ReadBytesExt>(reader: &mut T, num_colors: usize) -> Result<[u32; NUM_COLORS], PaletteError> {
|
||||
fn read_palette_6bit<T: ReadBytesExt>(reader: &mut T, num_colors: usize) -> Result<[RGBA; NUM_COLORS], PaletteError> {
|
||||
if num_colors > NUM_COLORS {
|
||||
return Err(PaletteError::OutOfRange(num_colors));
|
||||
}
|
||||
let mut colors = [to_argb32([255, 0, 0, 0]); NUM_COLORS];
|
||||
for i in 0..num_colors {
|
||||
let mut colors = [RGBA::from_rgba([0, 0, 0, 255]); NUM_COLORS];
|
||||
for color in colors.iter_mut().take(num_colors) {
|
||||
let r = reader.read_u8()?;
|
||||
let g = reader.read_u8()?;
|
||||
let b = reader.read_u8()?;
|
||||
let color = to_rgb32([from_6bit(r), from_6bit(g), from_6bit(b)]);
|
||||
colors[i] = color;
|
||||
*color = RGBA::from_rgb([from_6bit(r), from_6bit(g), from_6bit(b)]);
|
||||
}
|
||||
Ok(colors)
|
||||
}
|
||||
|
||||
fn write_palette_6bit<T: WriteBytesExt>(
|
||||
writer: &mut T,
|
||||
colors: &[u32; NUM_COLORS],
|
||||
colors: &[RGBA; NUM_COLORS],
|
||||
num_colors: usize,
|
||||
) -> Result<(), PaletteError> {
|
||||
if num_colors > NUM_COLORS {
|
||||
return Err(PaletteError::OutOfRange(num_colors));
|
||||
}
|
||||
for i in 0..num_colors {
|
||||
let [r, g, b] = from_rgb32(colors[i]);
|
||||
writer.write_u8(to_6bit(r))?;
|
||||
writer.write_u8(to_6bit(g))?;
|
||||
writer.write_u8(to_6bit(b))?;
|
||||
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()))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// normal (0-255) format
|
||||
fn read_palette_8bit<T: ReadBytesExt>(reader: &mut T, num_colors: usize) -> Result<[u32; NUM_COLORS], PaletteError> {
|
||||
fn read_palette_8bit<T: ReadBytesExt>(reader: &mut T, num_colors: usize) -> Result<[RGBA; NUM_COLORS], PaletteError> {
|
||||
if num_colors > NUM_COLORS {
|
||||
return Err(PaletteError::OutOfRange(num_colors));
|
||||
}
|
||||
let mut colors = [to_argb32([255, 0, 0, 0]); NUM_COLORS];
|
||||
for i in 0..num_colors {
|
||||
let mut colors = [RGBA::from_rgba([0, 0, 0, 255]); NUM_COLORS];
|
||||
for color in colors.iter_mut().take(num_colors) {
|
||||
let r = reader.read_u8()?;
|
||||
let g = reader.read_u8()?;
|
||||
let b = reader.read_u8()?;
|
||||
let color = to_rgb32([r, g, b]);
|
||||
colors[i] = color;
|
||||
*color = RGBA::from_rgb([r, g, b]);
|
||||
}
|
||||
Ok(colors)
|
||||
}
|
||||
|
||||
fn write_palette_8bit<T: WriteBytesExt>(
|
||||
writer: &mut T,
|
||||
colors: &[u32; NUM_COLORS],
|
||||
colors: &[RGBA; NUM_COLORS],
|
||||
num_colors: usize,
|
||||
) -> Result<(), PaletteError> {
|
||||
if num_colors > NUM_COLORS {
|
||||
return Err(PaletteError::OutOfRange(num_colors));
|
||||
}
|
||||
for i in 0..num_colors {
|
||||
let [r, g, b] = from_rgb32(colors[i]);
|
||||
writer.write_u8(r)?;
|
||||
writer.write_u8(g)?;
|
||||
writer.write_u8(b)?;
|
||||
for color in colors.iter().take(num_colors) {
|
||||
writer.write_u8(color.r())?;
|
||||
writer.write_u8(color.g())?;
|
||||
writer.write_u8(color.b())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -114,19 +110,18 @@ 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: [u32; NUM_COLORS],
|
||||
colors: [RGBA; NUM_COLORS],
|
||||
}
|
||||
|
||||
impl Palette {
|
||||
/// Creates a new Palette with all black colors.
|
||||
pub fn new() -> Palette {
|
||||
Palette { colors: [0; NUM_COLORS] }
|
||||
Palette { colors: [RGBA::from_rgb([0, 0, 0]); NUM_COLORS] }
|
||||
}
|
||||
|
||||
/// Creates a new Palette with all initial colors having the RGB values specified.
|
||||
pub fn new_with_default(r: u8, g: u8, b: u8) -> Palette {
|
||||
let rgb = to_rgb32([r, g, b]);
|
||||
Palette { colors: [rgb; NUM_COLORS] }
|
||||
Palette { colors: [RGBA::from_rgb([r, g, b]); NUM_COLORS] }
|
||||
}
|
||||
|
||||
/// Creates a new Palette, pre-loaded with the default VGA BIOS colors.
|
||||
|
@ -140,7 +135,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: &Path, format: PaletteFormat) -> Result<Palette, PaletteError> {
|
||||
pub fn load_from_file(path: impl AsRef<Path>, format: PaletteFormat) -> Result<Palette, PaletteError> {
|
||||
let f = File::open(path)?;
|
||||
let mut reader = BufReader::new(f);
|
||||
Self::load_from_bytes(&mut reader, format)
|
||||
|
@ -171,7 +166,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: &Path,
|
||||
path: impl AsRef<Path>,
|
||||
format: PaletteFormat,
|
||||
num_colors: usize,
|
||||
) -> Result<Palette, PaletteError> {
|
||||
|
@ -211,7 +206,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: &Path, format: PaletteFormat) -> Result<(), PaletteError> {
|
||||
pub fn to_file(&self, path: impl AsRef<Path>, format: PaletteFormat) -> Result<(), PaletteError> {
|
||||
let f = File::create(path)?;
|
||||
let mut writer = BufWriter::new(f);
|
||||
self.to_bytes(&mut writer, format)
|
||||
|
@ -242,7 +237,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: &Path,
|
||||
path: impl AsRef<Path>,
|
||||
format: PaletteFormat,
|
||||
num_colors: usize,
|
||||
) -> Result<(), PaletteError> {
|
||||
|
@ -295,7 +290,9 @@ 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, mut g, mut b] = from_rgb32(self.colors[color as usize]);
|
||||
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();
|
||||
|
||||
if r != target_r {
|
||||
modified = true;
|
||||
|
@ -328,7 +325,7 @@ impl Palette {
|
|||
}
|
||||
|
||||
if modified {
|
||||
self.colors[color as usize] = to_rgb32([r, g, b]);
|
||||
self.colors[color as usize] = RGBA::from_rgb([r, g, b]);
|
||||
}
|
||||
|
||||
(target_r == r) && (target_g == g) && (target_b == b)
|
||||
|
@ -381,8 +378,7 @@ 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 {
|
||||
let [r, g, b] = from_rgb32(palette[color]);
|
||||
if !self.fade_color_toward_rgb(color, r, g, b, step) {
|
||||
if !self.fade_color_toward_rgb(color, palette[color].r(), palette[color].g(), palette[color].b(), step) {
|
||||
all_faded = false;
|
||||
}
|
||||
}
|
||||
|
@ -400,7 +396,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] = lerp_rgb32(a[color], b[color], t);
|
||||
self[color] = a[color].lerp(b[color], t);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -439,15 +435,14 @@ impl Palette {
|
|||
let mut closest = 0;
|
||||
|
||||
for (index, color) in self.colors.iter().enumerate() {
|
||||
let [this_r, this_g, this_b] = from_rgb32(*color);
|
||||
|
||||
if r == this_r && g == this_g && b == this_b {
|
||||
if r == color.r() && g == color.g() && b == color.b() {
|
||||
return index as u8;
|
||||
} else {
|
||||
// this comparison method is using the sRGB Euclidean formula described here:
|
||||
// https://en.wikipedia.org/wiki/Color_difference
|
||||
|
||||
let distance = abs_diff(this_r, r) as u32 + abs_diff(this_g, g) as u32 + abs_diff(this_b, b) as u32;
|
||||
let distance =
|
||||
abs_diff(color.r(), r) as u32 + abs_diff(color.g(), g) as u32 + abs_diff(color.b(), b) as u32;
|
||||
|
||||
if distance < closest_distance {
|
||||
closest = index as u8;
|
||||
|
@ -474,8 +469,14 @@ impl Palette {
|
|||
}
|
||||
}
|
||||
|
||||
impl Default for Palette {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<u8> for Palette {
|
||||
type Output = u32;
|
||||
type Output = RGBA;
|
||||
|
||||
#[inline]
|
||||
fn index(&self, index: u8) -> &Self::Output {
|
||||
|
@ -502,18 +503,18 @@ mod tests {
|
|||
|
||||
const BASE_PATH: &str = "./test-assets/palette/";
|
||||
|
||||
fn test_file(file: &Path) -> PathBuf {
|
||||
fn test_file(file: impl AsRef<Path>) -> PathBuf {
|
||||
PathBuf::from(BASE_PATH).join(file)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_and_set_colors() {
|
||||
let mut palette = Palette::new();
|
||||
assert_eq!(0, palette[0]);
|
||||
assert_eq!(0, palette[1]);
|
||||
palette[0] = 0x11223344;
|
||||
assert_eq!(0x11223344, palette[0]);
|
||||
assert_eq!(0, palette[1]);
|
||||
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]);
|
||||
}
|
||||
|
||||
fn assert_ega_colors(palette: &Palette) {
|
||||
|
@ -541,7 +542,7 @@ mod tests {
|
|||
|
||||
// vga rgb format (6-bit)
|
||||
|
||||
let palette = Palette::load_from_file(test_file(Path::new("vga.pal")).as_path(), PaletteFormat::Vga)?;
|
||||
let palette = Palette::load_from_file(test_file("vga.pal"), PaletteFormat::Vga)?;
|
||||
assert_ega_colors(&palette);
|
||||
|
||||
let save_path = tmp_dir.path().join("test_save_vga_format.pal");
|
||||
|
@ -551,7 +552,7 @@ mod tests {
|
|||
|
||||
// normal rgb format (8-bit)
|
||||
|
||||
let palette = Palette::load_from_file(test_file(Path::new("dp2.pal")).as_path(), PaletteFormat::Normal)?;
|
||||
let palette = Palette::load_from_file(test_file("dp2.pal"), PaletteFormat::Normal)?;
|
||||
|
||||
let save_path = tmp_dir.path().join("test_save_normal_format.pal");
|
||||
palette.to_file(&save_path, PaletteFormat::Normal)?;
|
||||
|
@ -567,8 +568,7 @@ mod tests {
|
|||
|
||||
// vga rgb format (6-bit)
|
||||
|
||||
let palette =
|
||||
Palette::load_num_colors_from_file(test_file(Path::new("ega_6bit.pal")).as_path(), PaletteFormat::Vga, 16)?;
|
||||
let palette = Palette::load_num_colors_from_file(test_file("ega_6bit.pal"), PaletteFormat::Vga, 16)?;
|
||||
assert_ega_colors(&palette);
|
||||
|
||||
let save_path = tmp_dir.path().join("test_save_vga_format_16_colors.pal");
|
||||
|
@ -578,11 +578,7 @@ mod tests {
|
|||
|
||||
// normal rgb format (8-bit)
|
||||
|
||||
let palette = Palette::load_num_colors_from_file(
|
||||
test_file(Path::new("ega_8bit.pal")).as_path(),
|
||||
PaletteFormat::Normal,
|
||||
16,
|
||||
)?;
|
||||
let palette = Palette::load_num_colors_from_file(test_file("ega_8bit.pal"), PaletteFormat::Normal, 16)?;
|
||||
|
||||
let save_path = tmp_dir.path().join("test_save_normal_format_16_colors.pal");
|
||||
palette.to_file(&save_path, PaletteFormat::Normal)?;
|
||||
|
|
|
@ -21,7 +21,8 @@ mod tests {
|
|||
use std::io::{BufReader, Read};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use byteorder::{LittleEndian, ReadBytesExt};
|
||||
use crate::graphics::RGBA;
|
||||
use crate::utils::ReadType;
|
||||
|
||||
#[allow(dead_code)]
|
||||
const ASSETS_PATH: &str = "./assets/";
|
||||
|
@ -29,15 +30,15 @@ mod tests {
|
|||
const TEST_ASSETS_PATH: &str = "./test-assets/";
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn assets_file(file: &Path) -> PathBuf {
|
||||
pub fn assets_file(file: impl AsRef<Path>) -> PathBuf {
|
||||
PathBuf::from(ASSETS_PATH).join(file)
|
||||
}
|
||||
|
||||
pub fn test_assets_file(file: &Path) -> PathBuf {
|
||||
pub fn test_assets_file(file: impl AsRef<Path>) -> PathBuf {
|
||||
PathBuf::from(TEST_ASSETS_PATH).join(file)
|
||||
}
|
||||
|
||||
pub fn load_raw_indexed(bin_file: &Path) -> Result<Box<[u8]>, io::Error> {
|
||||
pub fn load_raw_indexed(bin_file: impl AsRef<Path>) -> Result<Box<[u8]>, io::Error> {
|
||||
let f = File::open(bin_file)?;
|
||||
let mut reader = BufReader::new(f);
|
||||
let mut buffer = Vec::new();
|
||||
|
@ -45,12 +46,12 @@ mod tests {
|
|||
Ok(buffer.into_boxed_slice())
|
||||
}
|
||||
|
||||
pub fn load_raw_argb(bin_file: &Path) -> Result<Box<[u32]>, io::Error> {
|
||||
pub fn load_raw_rgba(bin_file: impl AsRef<Path>) -> Result<Box<[RGBA]>, io::Error> {
|
||||
let f = File::open(bin_file)?;
|
||||
let mut reader = BufReader::new(f);
|
||||
let mut buffer = Vec::new();
|
||||
loop {
|
||||
buffer.push(match reader.read_u32::<LittleEndian>() {
|
||||
buffer.push(match RGBA::read(&mut reader) {
|
||||
Ok(value) => value,
|
||||
Err(err) if err.kind() == io::ErrorKind::UnexpectedEof => break,
|
||||
Err(err) => return Err(err),
|
||||
|
|
|
@ -24,8 +24,7 @@ impl Circle {
|
|||
let mut max_x = min_x;
|
||||
let mut max_y = min_y;
|
||||
|
||||
for i in 0..points.len() {
|
||||
let point = &points[i];
|
||||
for point in points.iter() {
|
||||
min_x = point.x.min(min_x);
|
||||
min_y = point.y.min(min_y);
|
||||
max_x = point.x.max(max_x);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::ops::{Add, Div, Mul, Sub};
|
||||
use std::simd;
|
||||
use std::simd::{SimdFloat, SimdPartialOrd};
|
||||
use std::simd::prelude::{SimdFloat, SimdPartialOrd};
|
||||
|
||||
mod circle;
|
||||
mod matrix3x3;
|
||||
|
@ -268,6 +268,7 @@ 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);
|
||||
|
|
|
@ -435,6 +435,7 @@ 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);
|
||||
|
|
|
@ -445,6 +445,12 @@ impl<ContextType> States<ContextType> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<ContextType> Default for States<ContextType> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use claim::*;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use byte_slice_cast::AsByteSlice;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::graphics::{IndexedBitmap, Palette, RgbaBitmap};
|
||||
use crate::graphics::{ColorsAsBytes, IndexedBitmap, Palette, RgbaBitmap, RGBA};
|
||||
|
||||
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;
|
||||
|
@ -24,7 +23,7 @@ pub enum SdlFramebufferError {
|
|||
pub struct SdlFramebuffer {
|
||||
sdl_texture: sdl2::render::Texture,
|
||||
sdl_texture_pitch: usize,
|
||||
intermediate_texture: Option<Box<[u32]>>,
|
||||
intermediate_texture: Option<Box<[RGBA]>>,
|
||||
}
|
||||
|
||||
// TODO: i'm not totally happy with this implementation. i don't like the two display methods and how the caller
|
||||
|
@ -46,14 +45,13 @@ impl SdlFramebuffer {
|
|||
return Err(SdlFramebufferError::SDLError(error.to_string()));
|
||||
}
|
||||
|
||||
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())),
|
||||
};
|
||||
let format = sdl2::pixels::PixelFormatEnum::ABGR8888;
|
||||
|
||||
let sdl_texture =
|
||||
match canvas.create_texture_streaming(Some(format), logical_screen_width, logical_screen_height) {
|
||||
Ok(texture) => texture,
|
||||
Err(error) => return Err(SdlFramebufferError::SDLError(error.to_string())),
|
||||
};
|
||||
let sdl_texture_pitch = sdl_texture.query().width as usize * SCREEN_TEXTURE_PIXEL_SIZE;
|
||||
|
||||
let intermediate_texture = if create_intermediate_texture {
|
||||
|
@ -61,9 +59,8 @@ 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 * SCREEN_TEXTURE_PIXEL_SIZE;
|
||||
Some(vec![0u32; texture_pixels_size].into_boxed_slice())
|
||||
let texture_pixels_size = (logical_screen_width * logical_screen_height) as usize;
|
||||
Some(vec![RGBA::default(); texture_pixels_size].into_boxed_slice())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
@ -81,9 +78,9 @@ impl SdlFramebuffer {
|
|||
"Calls to display_indexed_bitmap should only occur on SdlFramebuffers with an intermediate_texture",
|
||||
);
|
||||
|
||||
src.copy_as_argb_to(intermediate_texture, palette);
|
||||
src.copy_as_rgba_to(intermediate_texture, palette);
|
||||
|
||||
let texture_pixels = intermediate_texture.as_byte_slice();
|
||||
let texture_pixels = intermediate_texture.as_bytes();
|
||||
if let Err(error) = self.sdl_texture.update(None, texture_pixels, self.sdl_texture_pitch) {
|
||||
return Err(SdlFramebufferError::SDLError(error.to_string()));
|
||||
}
|
||||
|
@ -106,7 +103,7 @@ impl SdlFramebuffer {
|
|||
"Calls to display should only occur on SdlFramebuffers without an intermediate_texture"
|
||||
);
|
||||
|
||||
let texture_pixels = src.pixels().as_byte_slice();
|
||||
let texture_pixels = src.pixels().as_bytes();
|
||||
if let Err(error) = self.sdl_texture.update(None, texture_pixels, self.sdl_texture_pitch) {
|
||||
return Err(SdlFramebufferError::SDLError(error.to_string()));
|
||||
}
|
||||
|
|
|
@ -55,6 +55,12 @@ 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() {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::graphics::{GeneralBitmap, GeneralBlitMethod, IndexedBitmap, RgbaBitmap};
|
||||
use crate::graphics::{ColorsAsBytes, GeneralBitmap, GeneralBlitMethod, IndexedBitmap, RgbaBitmap, RGBA};
|
||||
use crate::math::Rect;
|
||||
use crate::system::Mouse;
|
||||
|
||||
|
@ -202,6 +202,16 @@ 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]
|
||||
|
@ -240,34 +250,34 @@ impl DefaultMouseCursorBitmaps<IndexedBitmap> for CustomMouseCursor<IndexedBitma
|
|||
impl DefaultMouseCursorBitmaps<RgbaBitmap> for CustomMouseCursor<RgbaBitmap> {
|
||||
fn get_default() -> MouseCursorBitmap<RgbaBitmap> {
|
||||
#[rustfmt::skip]
|
||||
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
|
||||
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
|
||||
];
|
||||
|
||||
let mut cursor =
|
||||
RgbaBitmap::new(DEFAULT_MOUSE_CURSOR_WIDTH as u32, DEFAULT_MOUSE_CURSOR_HEIGHT as u32).unwrap();
|
||||
cursor.pixels_mut().copy_from_slice(&CURSOR_PIXELS);
|
||||
cursor.pixels_mut().as_bytes_mut().copy_from_slice(&CURSOR_PIXELS);
|
||||
|
||||
MouseCursorBitmap {
|
||||
cursor,
|
||||
hotspot_x: DEFAULT_MOUSE_CURSOR_HOTSPOT_X,
|
||||
hotspot_y: DEFAULT_MOUSE_CURSOR_HOTSPOT_Y,
|
||||
transparent_color: 0xffff00ff,
|
||||
transparent_color: RGBA::from_rgba([255, 0, 255, 255]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -103,6 +103,12 @@ impl Mouse {
|
|||
}
|
||||
}
|
||||
|
||||
impl Default for Mouse {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl InputDevice for Mouse {
|
||||
fn update(&mut self) {
|
||||
self.x_delta = 0;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use thiserror::Error;
|
||||
|
||||
use crate::audio::AudioError;
|
||||
use crate::utils::app_root_dir;
|
||||
|
||||
mod event;
|
||||
mod framebuffer;
|
||||
|
@ -51,6 +52,9 @@ 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`].
|
||||
|
@ -195,6 +199,8 @@ impl SystemBuilder {
|
|||
|
||||
let event_pump = SystemEventPump::from(sdl_event_pump);
|
||||
|
||||
let app_root_dir = app_root_dir()?;
|
||||
|
||||
Ok(System {
|
||||
sdl_context,
|
||||
sdl_audio_subsystem,
|
||||
|
@ -202,6 +208,7 @@ 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,
|
||||
|
@ -210,6 +217,12 @@ 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.
|
||||
|
@ -231,6 +244,8 @@ where
|
|||
pub res: SystemResType,
|
||||
|
||||
pub event_pump: SystemEventPump,
|
||||
|
||||
pub app_root_dir: std::path::PathBuf,
|
||||
}
|
||||
|
||||
impl<SystemResType> std::fmt::Debug for System<SystemResType>
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
use std::io::{Error, SeekFrom};
|
||||
use byteorder::{ReadBytesExt, WriteBytesExt};
|
||||
use std::{
|
||||
io::{Error, SeekFrom},
|
||||
path::PathBuf,
|
||||
};
|
||||
|
||||
/// 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.
|
||||
|
@ -20,3 +24,41 @@ 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()
|
||||
}
|
||||
|
|
|
@ -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 in 0..initial_table_size {
|
||||
table[i] = Some(vec![i as u8]);
|
||||
for (i, item) in table.iter_mut().enumerate().take(initial_table_size) {
|
||||
*item = 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
Loading…
Reference in a new issue