large amount of bitmap graphics rework
this is an unfortunately large commit. probably should've been broken up into multiple smaller ones. i decided to revert some earlier preparatory work i had done to organize graphics functionality into two main streams: "indexed" and "rgb". this was going to result in two completely different Bitmap structs which were going to be largely similar and as i thought about it more, i realized my earlier thinking that i wouldn't be able to generify the Bitmap struct based on pixel-depth was actually not correct. so, this commit re-works things significantly in a way which i think is better but probably still not perfect. basically, we have one Bitmap struct which provides a lot of generic functionality, but also specialized implementations based on pixel-depth and this is now what is separated out into graphics::bitmap::indexed and graphics::bitmap::rgb. but the rest of the graphics functionality is not separated out by module like this any longer. note that i've still kept the GeneralBitmap trait and implementations. i was originally thinking i could get rid of this since i'd now made Bitmap generic over pixel-depth, but doing so would require me to rename the common/general blit methods to avoid a name collision with the specialized implementations (since they would both exist on the same types now). and i did not like that. i dunno, maybe i will change my mind later.
This commit is contained in:
parent
2b414072bc
commit
07f2b13f68
|
@ -2,7 +2,7 @@ use std::path::Path;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
use ggdt::prelude::dos_like::*;
|
use ggdt::prelude::*;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
struct AudioChannelStatus {
|
struct AudioChannelStatus {
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::path::Path;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
use ggdt::prelude::dos_like::*;
|
use ggdt::prelude::*;
|
||||||
|
|
||||||
const NUM_BALLS: usize = 128;
|
const NUM_BALLS: usize = 128;
|
||||||
const NUM_BALL_SPRITES: usize = 16;
|
const NUM_BALL_SPRITES: usize = 16;
|
||||||
|
@ -29,13 +29,13 @@ fn main() -> Result<()> {
|
||||||
let (balls_bmp, balls_palette) = Bitmap::load_pcx_file(Path::new("./assets/balls.pcx"))?;
|
let (balls_bmp, balls_palette) = Bitmap::load_pcx_file(Path::new("./assets/balls.pcx"))?;
|
||||||
system.res.palette = balls_palette.clone();
|
system.res.palette = balls_palette.clone();
|
||||||
|
|
||||||
let mut sprites = Vec::<Bitmap>::new();
|
let mut sprites = Vec::<IndexedBitmap>::new();
|
||||||
let mut balls = Vec::<Ball>::new();
|
let mut balls = Vec::<Ball>::new();
|
||||||
|
|
||||||
for i in 0..NUM_BALL_SPRITES {
|
for i in 0..NUM_BALL_SPRITES {
|
||||||
let mut sprite = Bitmap::new(BALL_WIDTH, BALL_HEIGHT)?;
|
let mut sprite = IndexedBitmap::new(BALL_WIDTH, BALL_HEIGHT)?;
|
||||||
sprite.blit_region(
|
sprite.blit_region(
|
||||||
BlitMethod::Solid,
|
IndexedBlitMethod::Solid,
|
||||||
&balls_bmp,
|
&balls_bmp,
|
||||||
&Rect::new(i as i32 * BALL_WIDTH as i32, 0, BALL_WIDTH, BALL_HEIGHT),
|
&Rect::new(i as i32 * BALL_WIDTH as i32, 0, BALL_WIDTH, BALL_HEIGHT),
|
||||||
0,
|
0,
|
||||||
|
@ -100,7 +100,7 @@ fn main() -> Result<()> {
|
||||||
|
|
||||||
for i in 0..NUM_BALLS {
|
for i in 0..NUM_BALLS {
|
||||||
system.res.video.blit(
|
system.res.video.blit(
|
||||||
BlitMethod::Transparent(0),
|
IndexedBlitMethod::Transparent(0),
|
||||||
&sprites[balls[i].sprite],
|
&sprites[balls[i].sprite],
|
||||||
balls[i].x,
|
balls[i].x,
|
||||||
balls[i].y,
|
balls[i].y,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use ggdt::prelude::dos_like::*;
|
use ggdt::prelude::*;
|
||||||
|
|
||||||
use crate::states::*;
|
use crate::states::*;
|
||||||
|
|
||||||
|
@ -161,7 +161,7 @@ fn render_system_sprites(context: &mut Context) {
|
||||||
for (entity, sprite_index) in sprite_indices.iter() {
|
for (entity, sprite_index) in sprite_indices.iter() {
|
||||||
let position = positions.get(&entity).unwrap();
|
let position = positions.get(&entity).unwrap();
|
||||||
context.system.res.video.blit(
|
context.system.res.video.blit(
|
||||||
BlitMethod::Transparent(0),
|
IndexedBlitMethod::Transparent(0),
|
||||||
&context.sprites[sprite_index.0],
|
&context.sprites[sprite_index.0],
|
||||||
position.0.x as i32,
|
position.0.x as i32,
|
||||||
position.0.y as i32,
|
position.0.y as i32,
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::path::Path;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
use ggdt::prelude::dos_like::*;
|
use ggdt::prelude::*;
|
||||||
|
|
||||||
use crate::entities::*;
|
use crate::entities::*;
|
||||||
use crate::states::*;
|
use crate::states::*;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use ggdt::prelude::dos_like::*;
|
use ggdt::prelude::*;
|
||||||
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ pub struct Context {
|
||||||
pub delta: f32,
|
pub delta: f32,
|
||||||
pub system: System<DosLike>,
|
pub system: System<DosLike>,
|
||||||
pub font: BitmaskFont,
|
pub font: BitmaskFont,
|
||||||
pub sprites: Vec<Bitmap>,
|
pub sprites: Vec<IndexedBitmap>,
|
||||||
pub entities: Entities,
|
pub entities: Entities,
|
||||||
pub event_publisher: EventPublisher<Event>,
|
pub event_publisher: EventPublisher<Event>,
|
||||||
}
|
}
|
||||||
|
@ -25,14 +25,14 @@ impl Game {
|
||||||
pub fn new(mut system: System<DosLike>) -> Result<Self> {
|
pub fn new(mut system: System<DosLike>) -> Result<Self> {
|
||||||
let font = BitmaskFont::new_vga_font()?;
|
let font = BitmaskFont::new_vga_font()?;
|
||||||
|
|
||||||
let (balls_bmp, balls_palette) = Bitmap::load_pcx_file(Path::new("./assets/balls.pcx"))?;
|
let (balls_bmp, balls_palette) = IndexedBitmap::load_pcx_file(Path::new("./assets/balls.pcx"))?;
|
||||||
system.res.palette = balls_palette.clone();
|
system.res.palette = balls_palette.clone();
|
||||||
|
|
||||||
let mut sprites = Vec::new();
|
let mut sprites = Vec::new();
|
||||||
for i in 0..NUM_BALL_SPRITES {
|
for i in 0..NUM_BALL_SPRITES {
|
||||||
let mut sprite = Bitmap::new(BALL_SIZE as u32, BALL_SIZE as u32)?;
|
let mut sprite = IndexedBitmap::new(BALL_SIZE as u32, BALL_SIZE as u32)?;
|
||||||
sprite.blit_region(
|
sprite.blit_region(
|
||||||
BlitMethod::Solid,
|
IndexedBlitMethod::Solid,
|
||||||
&balls_bmp,
|
&balls_bmp,
|
||||||
&Rect::new(i as i32 * BALL_SIZE as i32, 0, BALL_SIZE as u32, BALL_SIZE as u32),
|
&Rect::new(i as i32 * BALL_SIZE as i32, 0, BALL_SIZE as u32, BALL_SIZE as u32),
|
||||||
0,
|
0,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use ggdt::prelude::dos_like::*;
|
use ggdt::prelude::*;
|
||||||
|
|
||||||
use crate::Core;
|
use crate::Core;
|
||||||
use crate::entities::*;
|
use crate::entities::*;
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::collections::HashMap;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use ggdt::prelude::dos_like::*;
|
use ggdt::prelude::*;
|
||||||
|
|
||||||
use crate::{Core, Game, TILE_HEIGHT, TILE_WIDTH, TileMap};
|
use crate::{Core, Game, TILE_HEIGHT, TILE_WIDTH, TileMap};
|
||||||
|
|
||||||
|
@ -218,7 +218,7 @@ pub struct LifeTime(pub f32);
|
||||||
pub struct Pixel(pub u8);
|
pub struct Pixel(pub u8);
|
||||||
|
|
||||||
pub struct Sprite {
|
pub struct Sprite {
|
||||||
pub atlas: Rc<BitmapAtlas<Bitmap>>,
|
pub atlas: Rc<BitmapAtlas<IndexedBitmap>>,
|
||||||
pub index: usize,
|
pub index: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -314,7 +314,7 @@ pub struct SpriteIndexByDirection {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Weapon {
|
pub struct Weapon {
|
||||||
pub atlas: Rc<BitmapAtlas<Bitmap>>,
|
pub atlas: Rc<BitmapAtlas<IndexedBitmap>>,
|
||||||
pub base_index: usize,
|
pub base_index: usize,
|
||||||
pub offsets: [Vector2; 4],
|
pub offsets: [Vector2; 4],
|
||||||
pub damage: i32,
|
pub damage: i32,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use ggdt::prelude::dos_like::*;
|
use ggdt::prelude::*;
|
||||||
|
|
||||||
use crate::{Core, TILE_HEIGHT, TILE_WIDTH};
|
use crate::{Core, TILE_HEIGHT, TILE_WIDTH};
|
||||||
use crate::entities::*;
|
use crate::entities::*;
|
||||||
|
@ -662,7 +662,7 @@ fn render_system_sprites(context: &mut Core) {
|
||||||
// build up list of entities to be rendered with their positions so we can sort them
|
// build up list of entities to be rendered with their positions so we can sort them
|
||||||
// and render these entities with a proper y-based sort order
|
// and render these entities with a proper y-based sort order
|
||||||
for (entity, _) in sprites.iter() {
|
for (entity, _) in sprites.iter() {
|
||||||
let mut blit_method = BlitMethod::Transparent(0);
|
let mut blit_method = IndexedBlitMethod::Transparent(0);
|
||||||
|
|
||||||
// check for flicker effects
|
// check for flicker effects
|
||||||
if let Some(flicker) = timed_flickers.get(entity) {
|
if let Some(flicker) = timed_flickers.get(entity) {
|
||||||
|
@ -673,7 +673,7 @@ fn render_system_sprites(context: &mut Core) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
FlickerMethod::Color(draw_color) => {
|
FlickerMethod::Color(draw_color) => {
|
||||||
blit_method = BlitMethod::TransparentSingle {
|
blit_method = IndexedBlitMethod::TransparentSingle {
|
||||||
transparent_color: 0,
|
transparent_color: 0,
|
||||||
draw_color,
|
draw_color,
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,7 +6,7 @@ use std::rc::Rc;
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
|
|
||||||
use ggdt::prelude::dos_like::*;
|
use ggdt::prelude::*;
|
||||||
|
|
||||||
use crate::entities::*;
|
use crate::entities::*;
|
||||||
use crate::states::*;
|
use crate::states::*;
|
||||||
|
@ -29,24 +29,24 @@ pub struct Core {
|
||||||
pub event_publisher: EventPublisher<Event>,
|
pub event_publisher: EventPublisher<Event>,
|
||||||
pub palette: Palette,
|
pub palette: Palette,
|
||||||
pub fade_out_palette: Palette,
|
pub fade_out_palette: Palette,
|
||||||
pub tiles: Rc<BitmapAtlas<Bitmap>>,
|
pub tiles: Rc<BitmapAtlas<IndexedBitmap>>,
|
||||||
pub hero_male: Rc<BitmapAtlas<Bitmap>>,
|
pub hero_male: Rc<BitmapAtlas<IndexedBitmap>>,
|
||||||
pub hero_female: Rc<BitmapAtlas<Bitmap>>,
|
pub hero_female: Rc<BitmapAtlas<IndexedBitmap>>,
|
||||||
pub green_slime: Rc<BitmapAtlas<Bitmap>>,
|
pub green_slime: Rc<BitmapAtlas<IndexedBitmap>>,
|
||||||
pub blue_slime: Rc<BitmapAtlas<Bitmap>>,
|
pub blue_slime: Rc<BitmapAtlas<IndexedBitmap>>,
|
||||||
pub orange_slime: Rc<BitmapAtlas<Bitmap>>,
|
pub orange_slime: Rc<BitmapAtlas<IndexedBitmap>>,
|
||||||
pub fist: Rc<BitmapAtlas<Bitmap>>,
|
pub fist: Rc<BitmapAtlas<IndexedBitmap>>,
|
||||||
pub sword: Rc<BitmapAtlas<Bitmap>>,
|
pub sword: Rc<BitmapAtlas<IndexedBitmap>>,
|
||||||
pub particles: Rc<BitmapAtlas<Bitmap>>,
|
pub particles: Rc<BitmapAtlas<IndexedBitmap>>,
|
||||||
pub items: Rc<BitmapAtlas<Bitmap>>,
|
pub items: Rc<BitmapAtlas<IndexedBitmap>>,
|
||||||
pub ui: Rc<BitmapAtlas<Bitmap>>,
|
pub ui: Rc<BitmapAtlas<IndexedBitmap>>,
|
||||||
pub tilemap: TileMap,
|
pub tilemap: TileMap,
|
||||||
pub slime_activity_states: Rc<HashMap<EntityActivity, Rc<AnimationDef>>>,
|
pub slime_activity_states: Rc<HashMap<EntityActivity, Rc<AnimationDef>>>,
|
||||||
pub hero_activity_states: Rc<HashMap<EntityActivity, Rc<AnimationDef>>>,
|
pub hero_activity_states: Rc<HashMap<EntityActivity, Rc<AnimationDef>>>,
|
||||||
pub poof1_animation_def: Rc<AnimationDef>,
|
pub poof1_animation_def: Rc<AnimationDef>,
|
||||||
pub poof2_animation_def: Rc<AnimationDef>,
|
pub poof2_animation_def: Rc<AnimationDef>,
|
||||||
pub sparkles_animation_def: Rc<AnimationDef>,
|
pub sparkles_animation_def: Rc<AnimationDef>,
|
||||||
pub sprite_render_list: Vec<(EntityId, Vector2, BlitMethod)>,
|
pub sprite_render_list: Vec<(EntityId, Vector2, IndexedBlitMethod)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CoreState<DosLike> for Core {
|
impl CoreState<DosLike> for Core {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use ggdt::prelude::dos_like::*;
|
use ggdt::prelude::*;
|
||||||
|
|
||||||
use crate::entities::*;
|
use crate::entities::*;
|
||||||
use crate::Game;
|
use crate::Game;
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::path::Path;
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
|
|
||||||
use ggdt::prelude::dos_like::*;
|
use ggdt::prelude::*;
|
||||||
|
|
||||||
use crate::{Game, TILE_HEIGHT, TILE_WIDTH};
|
use crate::{Game, TILE_HEIGHT, TILE_WIDTH};
|
||||||
|
|
||||||
|
@ -14,40 +14,40 @@ pub fn load_font(path: &Path) -> Result<BitmaskFont> {
|
||||||
BitmaskFont::load_from_file(path).context(format!("Loading font: {:?}", path))
|
BitmaskFont::load_from_file(path).context(format!("Loading font: {:?}", path))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_bitmap_atlas_autogrid(path: &Path) -> Result<BitmapAtlas<Bitmap>> {
|
pub fn load_bitmap_atlas_autogrid(path: &Path) -> Result<BitmapAtlas<IndexedBitmap>> {
|
||||||
let (bmp, _) = Bitmap::load_file(path).context(format!("Loading bitmap atlas: {:?}", path))?;
|
let (bmp, _) = Bitmap::load_file(path).context(format!("Loading bitmap atlas: {:?}", path))?;
|
||||||
let mut atlas = BitmapAtlas::new(bmp);
|
let mut atlas = BitmapAtlas::new(bmp);
|
||||||
atlas.add_grid(TILE_WIDTH, TILE_HEIGHT)?;
|
atlas.add_grid(TILE_WIDTH, TILE_HEIGHT)?;
|
||||||
Ok(atlas)
|
Ok(atlas)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_bitmap_atlas(path: &Path) -> Result<BitmapAtlas<Bitmap>> {
|
pub fn load_bitmap_atlas(path: &Path) -> Result<BitmapAtlas<IndexedBitmap>> {
|
||||||
let (bmp, _) = Bitmap::load_file(path).context(format!("Loading bitmap atlas: {:?}", path))?;
|
let (bmp, _) = Bitmap::load_file(path).context(format!("Loading bitmap atlas: {:?}", path))?;
|
||||||
let atlas = BitmapAtlas::new(bmp);
|
let atlas = BitmapAtlas::new(bmp);
|
||||||
Ok(atlas)
|
Ok(atlas)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_window(dest: &mut Bitmap, ui: &BitmapAtlas<Bitmap>, left: i32, top: i32, right: i32, bottom: i32) {
|
pub fn draw_window(dest: &mut IndexedBitmap, ui: &BitmapAtlas<IndexedBitmap>, left: i32, top: i32, right: i32, bottom: i32) {
|
||||||
dest.filled_rect(left + 8, top + 8, right - 8, bottom - 8, 1);
|
dest.filled_rect(left + 8, top + 8, right - 8, bottom - 8, 1);
|
||||||
|
|
||||||
// corners
|
// corners
|
||||||
dest.blit_region(BlitMethod::Transparent(0), &ui.bitmap(), &ui[2], left, top);
|
dest.blit_region(IndexedBlitMethod::Transparent(0), &ui.bitmap(), &ui[2], left, top);
|
||||||
dest.blit_region(BlitMethod::Transparent(0), &ui.bitmap(), &ui[3], right - 8, top);
|
dest.blit_region(IndexedBlitMethod::Transparent(0), &ui.bitmap(), &ui[3], right - 8, top);
|
||||||
dest.blit_region(BlitMethod::Transparent(0), &ui.bitmap(), &ui[4], left, bottom - 8);
|
dest.blit_region(IndexedBlitMethod::Transparent(0), &ui.bitmap(), &ui[4], left, bottom - 8);
|
||||||
dest.blit_region(BlitMethod::Transparent(0), &ui.bitmap(), &ui[5], right - 8, bottom - 8);
|
dest.blit_region(IndexedBlitMethod::Transparent(0), &ui.bitmap(), &ui[5], right - 8, bottom - 8);
|
||||||
|
|
||||||
// top and bottom edges
|
// top and bottom edges
|
||||||
for i in 0..((right - left) / 8) - 2 {
|
for i in 0..((right - left) / 8) - 2 {
|
||||||
let x = left + 8 + (i * 8);
|
let x = left + 8 + (i * 8);
|
||||||
dest.blit_region(BlitMethod::Transparent(0), &ui.bitmap(), &ui[9], x, top);
|
dest.blit_region(IndexedBlitMethod::Transparent(0), &ui.bitmap(), &ui[9], x, top);
|
||||||
dest.blit_region(BlitMethod::Transparent(0), &ui.bitmap(), &ui[8], x, bottom - 8);
|
dest.blit_region(IndexedBlitMethod::Transparent(0), &ui.bitmap(), &ui[8], x, bottom - 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
// left and right edges
|
// left and right edges
|
||||||
for i in 0..((bottom - top) / 8) - 2 {
|
for i in 0..((bottom - top) / 8) - 2 {
|
||||||
let y = top + 8 + (i * 8);
|
let y = top + 8 + (i * 8);
|
||||||
dest.blit_region(BlitMethod::Transparent(0), &ui.bitmap(), &ui[6], left, y);
|
dest.blit_region(IndexedBlitMethod::Transparent(0), &ui.bitmap(), &ui[6], left, y);
|
||||||
dest.blit_region(BlitMethod::Transparent(0), &ui.bitmap(), &ui[7], right - 8, y);
|
dest.blit_region(IndexedBlitMethod::Transparent(0), &ui.bitmap(), &ui[7], right - 8, y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ use std::path::Path;
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use ggdt::prelude::dos_like::*;
|
use ggdt::prelude::*;
|
||||||
|
|
||||||
use crate::{TILE_HEIGHT, TILE_WIDTH};
|
use crate::{TILE_HEIGHT, TILE_WIDTH};
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ impl TileMap {
|
||||||
&self.layers[2]
|
&self.layers[2]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw(&self, dest: &mut Bitmap, tiles: &BitmapAtlas<Bitmap>, camera_x: i32, camera_y: i32) {
|
pub fn draw(&self, dest: &mut IndexedBitmap, tiles: &BitmapAtlas<IndexedBitmap>, camera_x: i32, camera_y: i32) {
|
||||||
let xt = camera_x / TILE_WIDTH as i32;
|
let xt = camera_x / TILE_WIDTH as i32;
|
||||||
let yt = camera_y / TILE_HEIGHT as i32;
|
let yt = camera_y / TILE_HEIGHT as i32;
|
||||||
let xp = camera_x % TILE_WIDTH as i32;
|
let xp = camera_x % TILE_WIDTH as i32;
|
||||||
|
@ -75,11 +75,11 @@ impl TileMap {
|
||||||
|
|
||||||
let lower = self.layers[0][index];
|
let lower = self.layers[0][index];
|
||||||
if lower >= 0 {
|
if lower >= 0 {
|
||||||
dest.blit_region(BlitMethod::Solid, tiles.bitmap(), &tiles[lower as usize], xd, yd);
|
dest.blit_region(IndexedBlitMethod::Solid, tiles.bitmap(), &tiles[lower as usize], xd, yd);
|
||||||
}
|
}
|
||||||
let upper = self.layers[1][index];
|
let upper = self.layers[1][index];
|
||||||
if upper >= 0 {
|
if upper >= 0 {
|
||||||
dest.blit_region(BlitMethod::Transparent(0), tiles.bitmap(), &tiles[upper as usize], xd, yd);
|
dest.blit_region(IndexedBlitMethod::Transparent(0), tiles.bitmap(), &tiles[upper as usize], xd, yd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
|
|
||||||
use ggdt::prelude::dos_like::*;
|
use ggdt::prelude::*;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
use ggdt::prelude::dos_like::*;
|
use ggdt::prelude::*;
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let config = DosLikeConfig::new();
|
let config = DosLikeConfig::new();
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use criterion::{black_box, Criterion, criterion_group, criterion_main};
|
use criterion::{black_box, Criterion, criterion_group, criterion_main};
|
||||||
|
|
||||||
use ggdt::prelude::dos_like::*;
|
use ggdt::prelude::*;
|
||||||
|
|
||||||
pub fn criterion_benchmark(c: &mut Criterion) {
|
pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
let mut source = Bitmap::new(SCREEN_WIDTH, SCREEN_HEIGHT).unwrap();
|
let mut source = IndexedBitmap::new(SCREEN_WIDTH, SCREEN_HEIGHT).unwrap();
|
||||||
let mut dest = vec![0u32; (SCREEN_WIDTH * SCREEN_HEIGHT * 4) as usize].into_boxed_slice();
|
let mut dest = vec![0u32; (SCREEN_WIDTH * SCREEN_HEIGHT * 4) as usize].into_boxed_slice();
|
||||||
let palette = Palette::new_vga_palette().unwrap();
|
let palette = Palette::new_vga_palette().unwrap();
|
||||||
|
|
||||||
|
|
|
@ -2,23 +2,23 @@ use std::path::Path;
|
||||||
|
|
||||||
use criterion::{black_box, Criterion, criterion_group, criterion_main};
|
use criterion::{black_box, Criterion, criterion_group, criterion_main};
|
||||||
|
|
||||||
use ggdt::prelude::dos_like::*;
|
use ggdt::prelude::*;
|
||||||
|
|
||||||
pub fn criterion_benchmark(c: &mut Criterion) {
|
pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
let mut framebuffer = Bitmap::new(320, 240).unwrap();
|
let mut framebuffer = IndexedBitmap::new(320, 240).unwrap();
|
||||||
let (bmp, _) = Bitmap::load_iff_file(Path::new("./test-assets/test-tiles.lbm")).unwrap();
|
let (bmp, _) = IndexedBitmap::load_iff_file(Path::new("./test-assets/test-tiles.lbm")).unwrap();
|
||||||
|
|
||||||
let mut solid_bmp = Bitmap::new(16, 16).unwrap();
|
let mut solid_bmp = IndexedBitmap::new(16, 16).unwrap();
|
||||||
solid_bmp.blit_region(BlitMethod::Solid, &bmp, &Rect::new(16, 16, 16, 16), 0, 0);
|
solid_bmp.blit_region(IndexedBlitMethod::Solid, &bmp, &Rect::new(16, 16, 16, 16), 0, 0);
|
||||||
let mut trans_bmp = Bitmap::new(16, 16).unwrap();
|
let mut trans_bmp = IndexedBitmap::new(16, 16).unwrap();
|
||||||
trans_bmp.blit_region(BlitMethod::Solid, &bmp, &Rect::new(160, 0, 16, 16), 0, 0);
|
trans_bmp.blit_region(IndexedBlitMethod::Solid, &bmp, &Rect::new(160, 0, 16, 16), 0, 0);
|
||||||
|
|
||||||
//////
|
//////
|
||||||
|
|
||||||
c.bench_function("blit_single_checked_solid", |b| {
|
c.bench_function("blit_single_checked_solid", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
framebuffer.blit(
|
framebuffer.blit(
|
||||||
black_box(BlitMethod::Solid),
|
black_box(IndexedBlitMethod::Solid),
|
||||||
black_box(&solid_bmp),
|
black_box(&solid_bmp),
|
||||||
black_box(100),
|
black_box(100),
|
||||||
black_box(100),
|
black_box(100),
|
||||||
|
@ -29,7 +29,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
c.bench_function("blit_single_unchecked_solid", |b| {
|
c.bench_function("blit_single_unchecked_solid", |b| {
|
||||||
b.iter(|| unsafe {
|
b.iter(|| unsafe {
|
||||||
framebuffer.blit_unchecked(
|
framebuffer.blit_unchecked(
|
||||||
black_box(BlitMethod::Solid),
|
black_box(IndexedBlitMethod::Solid),
|
||||||
black_box(&solid_bmp),
|
black_box(&solid_bmp),
|
||||||
black_box(100),
|
black_box(100),
|
||||||
black_box(100),
|
black_box(100),
|
||||||
|
@ -42,7 +42,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
c.bench_function("blit_single_checked_transparent", |b| {
|
c.bench_function("blit_single_checked_transparent", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
framebuffer.blit(
|
framebuffer.blit(
|
||||||
black_box(BlitMethod::Transparent(0)),
|
black_box(IndexedBlitMethod::Transparent(0)),
|
||||||
black_box(&trans_bmp),
|
black_box(&trans_bmp),
|
||||||
black_box(100),
|
black_box(100),
|
||||||
black_box(100),
|
black_box(100),
|
||||||
|
@ -53,7 +53,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
c.bench_function("blit_single_unchecked_transparent", |b| {
|
c.bench_function("blit_single_unchecked_transparent", |b| {
|
||||||
b.iter(|| unsafe {
|
b.iter(|| unsafe {
|
||||||
framebuffer.blit_unchecked(
|
framebuffer.blit_unchecked(
|
||||||
black_box(BlitMethod::Transparent(0)),
|
black_box(IndexedBlitMethod::Transparent(0)),
|
||||||
black_box(&trans_bmp),
|
black_box(&trans_bmp),
|
||||||
black_box(100),
|
black_box(100),
|
||||||
black_box(100),
|
black_box(100),
|
||||||
|
@ -66,7 +66,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
c.bench_function("blit_solid_flipped_not_flipped", |b| {
|
c.bench_function("blit_solid_flipped_not_flipped", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
framebuffer.blit(
|
framebuffer.blit(
|
||||||
black_box(BlitMethod::SolidFlipped {
|
black_box(IndexedBlitMethod::SolidFlipped {
|
||||||
horizontal_flip: false,
|
horizontal_flip: false,
|
||||||
vertical_flip: false,
|
vertical_flip: false,
|
||||||
}),
|
}),
|
||||||
|
@ -80,7 +80,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
c.bench_function("blit_solid_flipped_horizontally", |b| {
|
c.bench_function("blit_solid_flipped_horizontally", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
framebuffer.blit(
|
framebuffer.blit(
|
||||||
black_box(BlitMethod::SolidFlipped {
|
black_box(IndexedBlitMethod::SolidFlipped {
|
||||||
horizontal_flip: true,
|
horizontal_flip: true,
|
||||||
vertical_flip: false,
|
vertical_flip: false,
|
||||||
}),
|
}),
|
||||||
|
@ -94,7 +94,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
c.bench_function("blit_solid_flipped_vertically", |b| {
|
c.bench_function("blit_solid_flipped_vertically", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
framebuffer.blit(
|
framebuffer.blit(
|
||||||
black_box(BlitMethod::SolidFlipped {
|
black_box(IndexedBlitMethod::SolidFlipped {
|
||||||
horizontal_flip: false,
|
horizontal_flip: false,
|
||||||
vertical_flip: true,
|
vertical_flip: true,
|
||||||
}),
|
}),
|
||||||
|
@ -108,7 +108,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
c.bench_function("blit_solid_flipped_horizontally_and_vertically", |b| {
|
c.bench_function("blit_solid_flipped_horizontally_and_vertically", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
framebuffer.blit(
|
framebuffer.blit(
|
||||||
black_box(BlitMethod::SolidFlipped {
|
black_box(IndexedBlitMethod::SolidFlipped {
|
||||||
horizontal_flip: true,
|
horizontal_flip: true,
|
||||||
vertical_flip: true,
|
vertical_flip: true,
|
||||||
}),
|
}),
|
||||||
|
@ -124,7 +124,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
c.bench_function("blit_transparent_flipped_not_flipped", |b| {
|
c.bench_function("blit_transparent_flipped_not_flipped", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
framebuffer.blit(
|
framebuffer.blit(
|
||||||
black_box(BlitMethod::TransparentFlipped {
|
black_box(IndexedBlitMethod::TransparentFlipped {
|
||||||
transparent_color: 0,
|
transparent_color: 0,
|
||||||
horizontal_flip: false,
|
horizontal_flip: false,
|
||||||
vertical_flip: false,
|
vertical_flip: false,
|
||||||
|
@ -139,7 +139,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
c.bench_function("blit_transparent_flipped_horizontally", |b| {
|
c.bench_function("blit_transparent_flipped_horizontally", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
framebuffer.blit(
|
framebuffer.blit(
|
||||||
black_box(BlitMethod::TransparentFlipped {
|
black_box(IndexedBlitMethod::TransparentFlipped {
|
||||||
transparent_color: 0,
|
transparent_color: 0,
|
||||||
horizontal_flip: true,
|
horizontal_flip: true,
|
||||||
vertical_flip: false,
|
vertical_flip: false,
|
||||||
|
@ -154,7 +154,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
c.bench_function("blit_transparent_flipped_vertically", |b| {
|
c.bench_function("blit_transparent_flipped_vertically", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
framebuffer.blit(
|
framebuffer.blit(
|
||||||
black_box(BlitMethod::TransparentFlipped {
|
black_box(IndexedBlitMethod::TransparentFlipped {
|
||||||
transparent_color: 0,
|
transparent_color: 0,
|
||||||
horizontal_flip: false,
|
horizontal_flip: false,
|
||||||
vertical_flip: true,
|
vertical_flip: true,
|
||||||
|
@ -169,7 +169,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
c.bench_function("blit_transparent_flipped_horizontally_and_vertically", |b| {
|
c.bench_function("blit_transparent_flipped_horizontally_and_vertically", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
framebuffer.blit(
|
framebuffer.blit(
|
||||||
black_box(BlitMethod::TransparentFlipped {
|
black_box(IndexedBlitMethod::TransparentFlipped {
|
||||||
transparent_color: 0,
|
transparent_color: 0,
|
||||||
horizontal_flip: true,
|
horizontal_flip: true,
|
||||||
vertical_flip: true,
|
vertical_flip: true,
|
||||||
|
@ -186,7 +186,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
c.bench_function("blit_transparent_single_flipped_not_flipped", |b| {
|
c.bench_function("blit_transparent_single_flipped_not_flipped", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
framebuffer.blit(
|
framebuffer.blit(
|
||||||
black_box(BlitMethod::TransparentFlippedSingle {
|
black_box(IndexedBlitMethod::TransparentFlippedSingle {
|
||||||
transparent_color: 0,
|
transparent_color: 0,
|
||||||
horizontal_flip: false,
|
horizontal_flip: false,
|
||||||
vertical_flip: false,
|
vertical_flip: false,
|
||||||
|
@ -202,7 +202,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
c.bench_function("blit_transparent_single_flipped_horizontally", |b| {
|
c.bench_function("blit_transparent_single_flipped_horizontally", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
framebuffer.blit(
|
framebuffer.blit(
|
||||||
black_box(BlitMethod::TransparentFlippedSingle {
|
black_box(IndexedBlitMethod::TransparentFlippedSingle {
|
||||||
transparent_color: 0,
|
transparent_color: 0,
|
||||||
horizontal_flip: true,
|
horizontal_flip: true,
|
||||||
vertical_flip: false,
|
vertical_flip: false,
|
||||||
|
@ -218,7 +218,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
c.bench_function("blit_transparent_single_flipped_vertically", |b| {
|
c.bench_function("blit_transparent_single_flipped_vertically", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
framebuffer.blit(
|
framebuffer.blit(
|
||||||
black_box(BlitMethod::TransparentFlippedSingle {
|
black_box(IndexedBlitMethod::TransparentFlippedSingle {
|
||||||
transparent_color: 0,
|
transparent_color: 0,
|
||||||
horizontal_flip: false,
|
horizontal_flip: false,
|
||||||
vertical_flip: true,
|
vertical_flip: true,
|
||||||
|
@ -234,7 +234,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
c.bench_function("blit_transparent_single_flipped_horizontally_and_vertically", |b| {
|
c.bench_function("blit_transparent_single_flipped_horizontally_and_vertically", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
framebuffer.blit(
|
framebuffer.blit(
|
||||||
black_box(BlitMethod::TransparentFlippedSingle {
|
black_box(IndexedBlitMethod::TransparentFlippedSingle {
|
||||||
transparent_color: 0,
|
transparent_color: 0,
|
||||||
horizontal_flip: true,
|
horizontal_flip: true,
|
||||||
vertical_flip: true,
|
vertical_flip: true,
|
||||||
|
@ -252,7 +252,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
c.bench_function("blit_transparent_single", |b| {
|
c.bench_function("blit_transparent_single", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
framebuffer.blit(
|
framebuffer.blit(
|
||||||
black_box(BlitMethod::TransparentSingle {
|
black_box(IndexedBlitMethod::TransparentSingle {
|
||||||
transparent_color: 0,
|
transparent_color: 0,
|
||||||
draw_color: 17,
|
draw_color: 17,
|
||||||
}),
|
}),
|
||||||
|
@ -268,7 +268,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
c.bench_function("blit_transparent_offset", |b| {
|
c.bench_function("blit_transparent_offset", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
framebuffer.blit(
|
framebuffer.blit(
|
||||||
black_box(BlitMethod::TransparentOffset {
|
black_box(IndexedBlitMethod::TransparentOffset {
|
||||||
transparent_color: 0,
|
transparent_color: 0,
|
||||||
offset: 42,
|
offset: 42,
|
||||||
}),
|
}),
|
||||||
|
@ -284,7 +284,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
c.bench_function("blit_transparent_offset_flipped_not_flipped", |b| {
|
c.bench_function("blit_transparent_offset_flipped_not_flipped", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
framebuffer.blit(
|
framebuffer.blit(
|
||||||
black_box(BlitMethod::TransparentFlippedOffset {
|
black_box(IndexedBlitMethod::TransparentFlippedOffset {
|
||||||
transparent_color: 0,
|
transparent_color: 0,
|
||||||
horizontal_flip: false,
|
horizontal_flip: false,
|
||||||
vertical_flip: false,
|
vertical_flip: false,
|
||||||
|
@ -300,7 +300,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
c.bench_function("blit_transparent_offset_flipped_horizontally", |b| {
|
c.bench_function("blit_transparent_offset_flipped_horizontally", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
framebuffer.blit(
|
framebuffer.blit(
|
||||||
black_box(BlitMethod::TransparentFlippedOffset {
|
black_box(IndexedBlitMethod::TransparentFlippedOffset {
|
||||||
transparent_color: 0,
|
transparent_color: 0,
|
||||||
horizontal_flip: true,
|
horizontal_flip: true,
|
||||||
vertical_flip: false,
|
vertical_flip: false,
|
||||||
|
@ -316,7 +316,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
c.bench_function("blit_transparent_offset_flipped_vertically", |b| {
|
c.bench_function("blit_transparent_offset_flipped_vertically", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
framebuffer.blit(
|
framebuffer.blit(
|
||||||
black_box(BlitMethod::TransparentFlippedOffset {
|
black_box(IndexedBlitMethod::TransparentFlippedOffset {
|
||||||
transparent_color: 0,
|
transparent_color: 0,
|
||||||
horizontal_flip: false,
|
horizontal_flip: false,
|
||||||
vertical_flip: true,
|
vertical_flip: true,
|
||||||
|
@ -332,7 +332,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
c.bench_function("blit_transparent_offset_flipped_horizontally_and_vertically", |b| {
|
c.bench_function("blit_transparent_offset_flipped_horizontally_and_vertically", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
framebuffer.blit(
|
framebuffer.blit(
|
||||||
black_box(BlitMethod::TransparentFlippedOffset {
|
black_box(IndexedBlitMethod::TransparentFlippedOffset {
|
||||||
transparent_color: 0,
|
transparent_color: 0,
|
||||||
horizontal_flip: true,
|
horizontal_flip: true,
|
||||||
vertical_flip: true,
|
vertical_flip: true,
|
||||||
|
@ -350,7 +350,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
c.bench_function("blit_solid_offset", |b| {
|
c.bench_function("blit_solid_offset", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
framebuffer.blit(
|
framebuffer.blit(
|
||||||
black_box(BlitMethod::SolidOffset(42)),
|
black_box(IndexedBlitMethod::SolidOffset(42)),
|
||||||
black_box(&solid_bmp),
|
black_box(&solid_bmp),
|
||||||
black_box(100),
|
black_box(100),
|
||||||
black_box(100),
|
black_box(100),
|
||||||
|
@ -363,7 +363,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
c.bench_function("blit_solid_offset_flipped_not_flipped", |b| {
|
c.bench_function("blit_solid_offset_flipped_not_flipped", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
framebuffer.blit(
|
framebuffer.blit(
|
||||||
black_box(BlitMethod::SolidFlippedOffset {
|
black_box(IndexedBlitMethod::SolidFlippedOffset {
|
||||||
horizontal_flip: false,
|
horizontal_flip: false,
|
||||||
vertical_flip: false,
|
vertical_flip: false,
|
||||||
offset: 42,
|
offset: 42,
|
||||||
|
@ -378,7 +378,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
c.bench_function("blit_solid_offset_flipped_horizontally", |b| {
|
c.bench_function("blit_solid_offset_flipped_horizontally", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
framebuffer.blit(
|
framebuffer.blit(
|
||||||
black_box(BlitMethod::SolidFlippedOffset {
|
black_box(IndexedBlitMethod::SolidFlippedOffset {
|
||||||
horizontal_flip: true,
|
horizontal_flip: true,
|
||||||
vertical_flip: false,
|
vertical_flip: false,
|
||||||
offset: 42,
|
offset: 42,
|
||||||
|
@ -393,7 +393,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
c.bench_function("blit_solid_offset_flipped_vertically", |b| {
|
c.bench_function("blit_solid_offset_flipped_vertically", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
framebuffer.blit(
|
framebuffer.blit(
|
||||||
black_box(BlitMethod::SolidFlippedOffset {
|
black_box(IndexedBlitMethod::SolidFlippedOffset {
|
||||||
horizontal_flip: false,
|
horizontal_flip: false,
|
||||||
vertical_flip: true,
|
vertical_flip: true,
|
||||||
offset: 42,
|
offset: 42,
|
||||||
|
@ -408,7 +408,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
c.bench_function("blit_solid_offset_flipped_horizontally_and_vertically", |b| {
|
c.bench_function("blit_solid_offset_flipped_horizontally_and_vertically", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
framebuffer.blit(
|
framebuffer.blit(
|
||||||
black_box(BlitMethod::SolidFlippedOffset {
|
black_box(IndexedBlitMethod::SolidFlippedOffset {
|
||||||
horizontal_flip: true,
|
horizontal_flip: true,
|
||||||
vertical_flip: true,
|
vertical_flip: true,
|
||||||
offset: 42,
|
offset: 42,
|
||||||
|
@ -425,7 +425,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
c.bench_function("blit_rotozoom", |b| {
|
c.bench_function("blit_rotozoom", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
framebuffer.blit(
|
framebuffer.blit(
|
||||||
black_box(BlitMethod::RotoZoom {
|
black_box(IndexedBlitMethod::RotoZoom {
|
||||||
angle: 73.0f32.to_radians(),
|
angle: 73.0f32.to_radians(),
|
||||||
scale_x: 1.2,
|
scale_x: 1.2,
|
||||||
scale_y: 0.8,
|
scale_y: 0.8,
|
||||||
|
@ -442,7 +442,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
c.bench_function("blit_rotozoom_offset", |b| {
|
c.bench_function("blit_rotozoom_offset", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
framebuffer.blit(
|
framebuffer.blit(
|
||||||
black_box(BlitMethod::RotoZoomOffset {
|
black_box(IndexedBlitMethod::RotoZoomOffset {
|
||||||
angle: 73.0f32.to_radians(),
|
angle: 73.0f32.to_radians(),
|
||||||
scale_x: 1.2,
|
scale_x: 1.2,
|
||||||
scale_y: 0.8,
|
scale_y: 0.8,
|
||||||
|
@ -460,7 +460,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
c.bench_function("blit_rotozoom_transparent", |b| {
|
c.bench_function("blit_rotozoom_transparent", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
framebuffer.blit(
|
framebuffer.blit(
|
||||||
black_box(BlitMethod::RotoZoomTransparent {
|
black_box(IndexedBlitMethod::RotoZoomTransparent {
|
||||||
angle: 73.0f32.to_radians(),
|
angle: 73.0f32.to_radians(),
|
||||||
scale_x: 1.2,
|
scale_x: 1.2,
|
||||||
scale_y: 0.8,
|
scale_y: 0.8,
|
||||||
|
@ -478,7 +478,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
c.bench_function("blit_rotozoom_offset_transparent", |b| {
|
c.bench_function("blit_rotozoom_offset_transparent", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
framebuffer.blit(
|
framebuffer.blit(
|
||||||
black_box(BlitMethod::RotoZoomTransparentOffset {
|
black_box(IndexedBlitMethod::RotoZoomTransparentOffset {
|
||||||
angle: 73.0f32.to_radians(),
|
angle: 73.0f32.to_radians(),
|
||||||
scale_x: 1.2,
|
scale_x: 1.2,
|
||||||
scale_y: 0.8,
|
scale_y: 0.8,
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::io::Cursor;
|
||||||
|
|
||||||
use criterion::{black_box, Criterion, criterion_group, criterion_main};
|
use criterion::{black_box, Criterion, criterion_group, criterion_main};
|
||||||
|
|
||||||
use ggdt::prelude::dos_like::*;
|
use ggdt::prelude::*;
|
||||||
|
|
||||||
pub static SMALL_GIF_FILE_BYTES: &[u8] = include_bytes!("../test-assets/test.gif");
|
pub static SMALL_GIF_FILE_BYTES: &[u8] = include_bytes!("../test-assets/test.gif");
|
||||||
pub static LARGE_GIF_FILE_BYTES: &[u8] = include_bytes!("../test-assets/test_image.gif");
|
pub static LARGE_GIF_FILE_BYTES: &[u8] = include_bytes!("../test-assets/test_image.gif");
|
||||||
|
@ -11,14 +11,14 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
c.bench_function("loading_small_gif", |b| {
|
c.bench_function("loading_small_gif", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
let mut reader = Cursor::new(SMALL_GIF_FILE_BYTES);
|
let mut reader = Cursor::new(SMALL_GIF_FILE_BYTES);
|
||||||
Bitmap::load_gif_bytes(black_box(&mut reader)).unwrap();
|
IndexedBitmap::load_gif_bytes(black_box(&mut reader)).unwrap();
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
c.bench_function("loading_large_gif", |b| {
|
c.bench_function("loading_large_gif", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
let mut reader = Cursor::new(LARGE_GIF_FILE_BYTES);
|
let mut reader = Cursor::new(LARGE_GIF_FILE_BYTES);
|
||||||
Bitmap::load_gif_bytes(black_box(&mut reader)).unwrap();
|
IndexedBitmap::load_gif_bytes(black_box(&mut reader)).unwrap();
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,127 +1,7 @@
|
||||||
use std::rc::Rc;
|
use crate::graphics::bitmap::Bitmap;
|
||||||
|
use crate::graphics::Pixel;
|
||||||
use crate::graphics::bitmapatlas::BitmapAtlas;
|
|
||||||
use crate::graphics::indexed::bitmap::Bitmap;
|
|
||||||
use crate::graphics::indexed::blendmap::BlendMap;
|
|
||||||
use crate::math::rect::Rect;
|
use crate::math::rect::Rect;
|
||||||
|
|
||||||
#[derive(Clone, PartialEq)]
|
|
||||||
pub enum BlitMethod {
|
|
||||||
/// Solid blit, no transparency or other per-pixel adjustments.
|
|
||||||
Solid,
|
|
||||||
/// Same as [BlitMethod::Solid] but the drawn image can also be flipped horizontally
|
|
||||||
/// and/or vertically.
|
|
||||||
SolidFlipped {
|
|
||||||
horizontal_flip: bool,
|
|
||||||
vertical_flip: bool,
|
|
||||||
},
|
|
||||||
/// Transparent blit, the specified source color pixels are skipped.
|
|
||||||
Transparent(u8),
|
|
||||||
/// Same as [BlitMethod::Transparent] but the drawn image can also be flipped horizontally
|
|
||||||
/// and/or vertically.
|
|
||||||
TransparentFlipped {
|
|
||||||
transparent_color: u8,
|
|
||||||
horizontal_flip: bool,
|
|
||||||
vertical_flip: bool,
|
|
||||||
},
|
|
||||||
/// Same as [BlitMethod::Transparent] except that the visible pixels on the destination are all
|
|
||||||
/// drawn using the same color.
|
|
||||||
TransparentSingle {
|
|
||||||
transparent_color: u8,
|
|
||||||
draw_color: u8,
|
|
||||||
},
|
|
||||||
/// Combination of [BlitMethod::TransparentFlipped] and [BlitMethod::TransparentSingle].
|
|
||||||
TransparentFlippedSingle {
|
|
||||||
transparent_color: u8,
|
|
||||||
horizontal_flip: bool,
|
|
||||||
vertical_flip: bool,
|
|
||||||
draw_color: u8,
|
|
||||||
},
|
|
||||||
/// Same as [BlitMethod::Solid] except that the drawn pixels have their color indices offset
|
|
||||||
/// by the amount given.
|
|
||||||
SolidOffset(u8),
|
|
||||||
/// Combination of [BlitMethod::SolidFlipped] and [BlitMethod::SolidOffset].
|
|
||||||
SolidFlippedOffset {
|
|
||||||
horizontal_flip: bool,
|
|
||||||
vertical_flip: bool,
|
|
||||||
offset: u8,
|
|
||||||
},
|
|
||||||
/// Same as [BlitMethod::Transparent] except that the drawn pixels have their color indices
|
|
||||||
/// offset by the amount given. The transparent color check is not affected by the offset and
|
|
||||||
/// is always treated as an absolute palette color index.
|
|
||||||
TransparentOffset { transparent_color: u8, offset: u8 },
|
|
||||||
/// Combination of [BlitMethod::TransparentFlipped] and [BlitMethod::TransparentOffset].
|
|
||||||
TransparentFlippedOffset {
|
|
||||||
transparent_color: u8,
|
|
||||||
horizontal_flip: bool,
|
|
||||||
vertical_flip: bool,
|
|
||||||
offset: u8,
|
|
||||||
},
|
|
||||||
/// Rotozoom blit, works the same as [BlitMethod::Solid] except that rotation and scaling is
|
|
||||||
/// performed.
|
|
||||||
RotoZoom {
|
|
||||||
angle: f32,
|
|
||||||
scale_x: f32,
|
|
||||||
scale_y: f32,
|
|
||||||
},
|
|
||||||
/// Same as [BlitMethod::RotoZoom] except that the specified source color pixels are skipped.
|
|
||||||
RotoZoomTransparent {
|
|
||||||
angle: f32,
|
|
||||||
scale_x: f32,
|
|
||||||
scale_y: f32,
|
|
||||||
transparent_color: u8,
|
|
||||||
},
|
|
||||||
/// Same as [BlitMethod::RotoZoom] except that the drawn pixels have their color indices
|
|
||||||
/// offset by the amount given.
|
|
||||||
RotoZoomOffset {
|
|
||||||
angle: f32,
|
|
||||||
scale_x: f32,
|
|
||||||
scale_y: f32,
|
|
||||||
offset: u8,
|
|
||||||
},
|
|
||||||
/// Same as [BlitMethod::RotoZoomTransparent] except that the drawn pixels have their color
|
|
||||||
/// indices offset by the amount given. The transparent color check is not affected by the
|
|
||||||
/// offset and is always treated as an absolute palette color index.
|
|
||||||
RotoZoomTransparentOffset {
|
|
||||||
angle: f32,
|
|
||||||
scale_x: f32,
|
|
||||||
scale_y: f32,
|
|
||||||
transparent_color: u8,
|
|
||||||
offset: u8,
|
|
||||||
},
|
|
||||||
SolidBlended {
|
|
||||||
blend_map: Rc<BlendMap>,
|
|
||||||
},
|
|
||||||
SolidFlippedBlended {
|
|
||||||
horizontal_flip: bool,
|
|
||||||
vertical_flip: bool,
|
|
||||||
blend_map: Rc<BlendMap>,
|
|
||||||
},
|
|
||||||
TransparentBlended {
|
|
||||||
transparent_color: u8,
|
|
||||||
blend_map: Rc<BlendMap>,
|
|
||||||
},
|
|
||||||
TransparentFlippedBlended {
|
|
||||||
transparent_color: u8,
|
|
||||||
horizontal_flip: bool,
|
|
||||||
vertical_flip: bool,
|
|
||||||
blend_map: Rc<BlendMap>,
|
|
||||||
},
|
|
||||||
RotoZoomBlended {
|
|
||||||
angle: f32,
|
|
||||||
scale_x: f32,
|
|
||||||
scale_y: f32,
|
|
||||||
blend_map: Rc<BlendMap>,
|
|
||||||
},
|
|
||||||
RotoZoomTransparentBlended {
|
|
||||||
angle: f32,
|
|
||||||
scale_x: f32,
|
|
||||||
scale_y: f32,
|
|
||||||
transparent_color: u8,
|
|
||||||
blend_map: Rc<BlendMap>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Clips the region for a source bitmap to be used in a subsequent blit operation. The source
|
/// Clips the region for a source bitmap to be used in a subsequent blit operation. The source
|
||||||
/// region will be clipped against the clipping region given for the destination bitmap. The
|
/// region will be clipped against the clipping region given for the destination bitmap. The
|
||||||
/// top-left coordinates of the location to blit to on the destination bitmap are also adjusted
|
/// top-left coordinates of the location to blit to on the destination bitmap are also adjusted
|
||||||
|
@ -212,8 +92,8 @@ pub fn clip_blit(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn get_flipped_blit_properties(
|
fn get_flipped_blit_properties<PixelType: Pixel>(
|
||||||
src: &Bitmap,
|
src: &Bitmap<PixelType>,
|
||||||
src_region: &Rect,
|
src_region: &Rect,
|
||||||
horizontal_flip: bool,
|
horizontal_flip: bool,
|
||||||
vertical_flip: bool,
|
vertical_flip: bool,
|
||||||
|
@ -249,13 +129,13 @@ fn get_flipped_blit_properties(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn per_pixel_blit(
|
pub unsafe fn per_pixel_blit<PixelType: Pixel>(
|
||||||
dest: &mut Bitmap,
|
dest: &mut Bitmap<PixelType>,
|
||||||
src: &Bitmap,
|
src: &Bitmap<PixelType>,
|
||||||
src_region: &Rect,
|
src_region: &Rect,
|
||||||
dest_x: i32,
|
dest_x: i32,
|
||||||
dest_y: i32,
|
dest_y: i32,
|
||||||
pixel_fn: impl Fn(*const u8, *mut u8),
|
pixel_fn: impl Fn(*const PixelType, *mut PixelType),
|
||||||
) {
|
) {
|
||||||
let src_next_row_inc = (src.width - src_region.width) as usize;
|
let src_next_row_inc = (src.width - src_region.width) as usize;
|
||||||
let dest_next_row_inc = (dest.width - src_region.width) as usize;
|
let dest_next_row_inc = (dest.width - src_region.width) as usize;
|
||||||
|
@ -275,15 +155,15 @@ unsafe fn per_pixel_blit(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn per_pixel_flipped_blit(
|
pub unsafe fn per_pixel_flipped_blit<PixelType: Pixel>(
|
||||||
dest: &mut Bitmap,
|
dest: &mut Bitmap<PixelType>,
|
||||||
src: &Bitmap,
|
src: &Bitmap<PixelType>,
|
||||||
src_region: &Rect,
|
src_region: &Rect,
|
||||||
dest_x: i32,
|
dest_x: i32,
|
||||||
dest_y: i32,
|
dest_y: i32,
|
||||||
horizontal_flip: bool,
|
horizontal_flip: bool,
|
||||||
vertical_flip: bool,
|
vertical_flip: bool,
|
||||||
pixel_fn: impl Fn(*const u8, *mut u8),
|
pixel_fn: impl Fn(*const PixelType, *mut PixelType),
|
||||||
) {
|
) {
|
||||||
let dest_next_row_inc = (dest.width - src_region.width) as usize;
|
let dest_next_row_inc = (dest.width - src_region.width) as usize;
|
||||||
let (x_inc, src_start_x, src_start_y, src_next_row_inc) =
|
let (x_inc, src_start_x, src_start_y, src_next_row_inc) =
|
||||||
|
@ -305,16 +185,16 @@ unsafe fn per_pixel_flipped_blit(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn per_pixel_rotozoom_blit(
|
pub unsafe fn per_pixel_rotozoom_blit<PixelType: Pixel>(
|
||||||
dest: &mut Bitmap,
|
dest: &mut Bitmap<PixelType>,
|
||||||
src: &Bitmap,
|
src: &Bitmap<PixelType>,
|
||||||
src_region: &Rect,
|
src_region: &Rect,
|
||||||
dest_x: i32,
|
dest_x: i32,
|
||||||
dest_y: i32,
|
dest_y: i32,
|
||||||
angle: f32,
|
angle: f32,
|
||||||
scale_x: f32,
|
scale_x: f32,
|
||||||
scale_y: f32,
|
scale_y: f32,
|
||||||
pixel_fn: impl Fn(u8, &mut Bitmap, i32, i32),
|
pixel_fn: impl Fn(PixelType, &mut Bitmap<PixelType>, i32, i32),
|
||||||
) {
|
) {
|
||||||
let dest_width = src_region.width as f32 * scale_x;
|
let dest_width = src_region.width as f32 * scale_x;
|
||||||
let dest_height = src_region.height as f32 * scale_y;
|
let dest_height = src_region.height as f32 * scale_y;
|
||||||
|
@ -393,11 +273,11 @@ unsafe fn per_pixel_rotozoom_blit(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Bitmap {
|
impl<PixelType: Pixel> Bitmap<PixelType> {
|
||||||
pub unsafe fn solid_blit(&mut self, src: &Bitmap, src_region: &Rect, dest_x: i32, dest_y: i32) {
|
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_row_length = src_region.width as usize * Self::PIXEL_SIZE;
|
||||||
let src_pitch = src.width as usize;
|
let src_pitch = src.width as usize * Self::PIXEL_SIZE;
|
||||||
let dest_pitch = self.width as usize;
|
let dest_pitch = self.width as usize * Self::PIXEL_SIZE;
|
||||||
let mut src_pixels = src.pixels_at_ptr_unchecked(src_region.x, src_region.y);
|
let mut src_pixels = src.pixels_at_ptr_unchecked(src_region.x, src_region.y);
|
||||||
let mut dest_pixels = self.pixels_at_mut_ptr_unchecked(dest_x, dest_y);
|
let mut dest_pixels = self.pixels_at_mut_ptr_unchecked(dest_x, dest_y);
|
||||||
|
|
||||||
|
@ -408,29 +288,9 @@ impl Bitmap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn solid_blended_blit(
|
|
||||||
&mut self,
|
|
||||||
src: &Bitmap,
|
|
||||||
src_region: &Rect,
|
|
||||||
dest_x: i32,
|
|
||||||
dest_y: i32,
|
|
||||||
blend_map: Rc<BlendMap>,
|
|
||||||
) {
|
|
||||||
per_pixel_blit(
|
|
||||||
self, src, src_region, dest_x, dest_y,
|
|
||||||
|src_pixels, dest_pixels| {
|
|
||||||
if let Some(blended_pixel) = blend_map.blend(*src_pixels, *dest_pixels) {
|
|
||||||
*dest_pixels = blended_pixel;
|
|
||||||
} else {
|
|
||||||
*dest_pixels = *src_pixels;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn solid_flipped_blit(
|
pub unsafe fn solid_flipped_blit(
|
||||||
&mut self,
|
&mut self,
|
||||||
src: &Bitmap,
|
src: &Self,
|
||||||
src_region: &Rect,
|
src_region: &Rect,
|
||||||
dest_x: i32,
|
dest_x: i32,
|
||||||
dest_y: i32,
|
dest_y: i32,
|
||||||
|
@ -445,69 +305,13 @@ impl Bitmap {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn solid_flipped_blended_blit(
|
|
||||||
&mut self,
|
|
||||||
src: &Bitmap,
|
|
||||||
src_region: &Rect,
|
|
||||||
dest_x: i32,
|
|
||||||
dest_y: i32,
|
|
||||||
horizontal_flip: bool,
|
|
||||||
vertical_flip: bool,
|
|
||||||
blend_map: Rc<BlendMap>,
|
|
||||||
) {
|
|
||||||
per_pixel_flipped_blit(
|
|
||||||
self, src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip,
|
|
||||||
|src_pixels, dest_pixels| {
|
|
||||||
if let Some(blended_pixel) = blend_map.blend(*src_pixels, *dest_pixels) {
|
|
||||||
*dest_pixels = blended_pixel;
|
|
||||||
} else {
|
|
||||||
*dest_pixels = *src_pixels;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn solid_palette_offset_blit(
|
|
||||||
&mut self,
|
|
||||||
src: &Bitmap,
|
|
||||||
src_region: &Rect,
|
|
||||||
dest_x: i32,
|
|
||||||
dest_y: i32,
|
|
||||||
offset: u8,
|
|
||||||
) {
|
|
||||||
per_pixel_blit(
|
|
||||||
self, src, src_region, dest_x, dest_y,
|
|
||||||
|src_pixels, dest_pixels| {
|
|
||||||
*dest_pixels = (*src_pixels).wrapping_add(offset);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn solid_flipped_palette_offset_blit(
|
|
||||||
&mut self,
|
|
||||||
src: &Bitmap,
|
|
||||||
src_region: &Rect,
|
|
||||||
dest_x: i32,
|
|
||||||
dest_y: i32,
|
|
||||||
horizontal_flip: bool,
|
|
||||||
vertical_flip: bool,
|
|
||||||
offset: u8,
|
|
||||||
) {
|
|
||||||
per_pixel_flipped_blit(
|
|
||||||
self, src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip,
|
|
||||||
|src_pixels, dest_pixels| {
|
|
||||||
*dest_pixels = (*src_pixels).wrapping_add(offset);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn transparent_blit(
|
pub unsafe fn transparent_blit(
|
||||||
&mut self,
|
&mut self,
|
||||||
src: &Bitmap,
|
src: &Self,
|
||||||
src_region: &Rect,
|
src_region: &Rect,
|
||||||
dest_x: i32,
|
dest_x: i32,
|
||||||
dest_y: i32,
|
dest_y: i32,
|
||||||
transparent_color: u8,
|
transparent_color: PixelType,
|
||||||
) {
|
) {
|
||||||
per_pixel_blit(
|
per_pixel_blit(
|
||||||
self, src, src_region, dest_x, dest_y,
|
self, src, src_region, dest_x, dest_y,
|
||||||
|
@ -519,36 +323,13 @@ impl Bitmap {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn transparent_blended_blit(
|
|
||||||
&mut self,
|
|
||||||
src: &Bitmap,
|
|
||||||
src_region: &Rect,
|
|
||||||
dest_x: i32,
|
|
||||||
dest_y: i32,
|
|
||||||
transparent_color: u8,
|
|
||||||
blend_map: Rc<BlendMap>,
|
|
||||||
) {
|
|
||||||
per_pixel_blit(
|
|
||||||
self, src, src_region, dest_x, dest_y,
|
|
||||||
|src_pixels, dest_pixels| {
|
|
||||||
if *src_pixels != transparent_color {
|
|
||||||
if let Some(blended_pixel) = blend_map.blend(*src_pixels, *dest_pixels) {
|
|
||||||
*dest_pixels = blended_pixel;
|
|
||||||
} else {
|
|
||||||
*dest_pixels = *src_pixels;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn transparent_flipped_blit(
|
pub unsafe fn transparent_flipped_blit(
|
||||||
&mut self,
|
&mut self,
|
||||||
src: &Bitmap,
|
src: &Self,
|
||||||
src_region: &Rect,
|
src_region: &Rect,
|
||||||
dest_x: i32,
|
dest_x: i32,
|
||||||
dest_y: i32,
|
dest_y: i32,
|
||||||
transparent_color: u8,
|
transparent_color: PixelType,
|
||||||
horizontal_flip: bool,
|
horizontal_flip: bool,
|
||||||
vertical_flip: bool,
|
vertical_flip: bool,
|
||||||
) {
|
) {
|
||||||
|
@ -562,79 +343,14 @@ impl Bitmap {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn transparent_flipped_blended_blit(
|
|
||||||
&mut self,
|
|
||||||
src: &Bitmap,
|
|
||||||
src_region: &Rect,
|
|
||||||
dest_x: i32,
|
|
||||||
dest_y: i32,
|
|
||||||
transparent_color: u8,
|
|
||||||
horizontal_flip: bool,
|
|
||||||
vertical_flip: bool,
|
|
||||||
blend_map: Rc<BlendMap>,
|
|
||||||
) {
|
|
||||||
per_pixel_flipped_blit(
|
|
||||||
self, src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip,
|
|
||||||
|src_pixels, dest_pixels| {
|
|
||||||
if *src_pixels != transparent_color {
|
|
||||||
if let Some(blended_pixel) = blend_map.blend(*src_pixels, *dest_pixels) {
|
|
||||||
*dest_pixels = blended_pixel;
|
|
||||||
} else {
|
|
||||||
*dest_pixels = *src_pixels;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn transparent_palette_offset_blit(
|
|
||||||
&mut self,
|
|
||||||
src: &Bitmap,
|
|
||||||
src_region: &Rect,
|
|
||||||
dest_x: i32,
|
|
||||||
dest_y: i32,
|
|
||||||
transparent_color: u8,
|
|
||||||
offset: u8,
|
|
||||||
) {
|
|
||||||
per_pixel_blit(
|
|
||||||
self, src, src_region, dest_x, dest_y,
|
|
||||||
|src_pixels, dest_pixels| {
|
|
||||||
if *src_pixels != transparent_color {
|
|
||||||
*dest_pixels = (*src_pixels).wrapping_add(offset);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn transparent_flipped_palette_offset_blit(
|
|
||||||
&mut self,
|
|
||||||
src: &Bitmap,
|
|
||||||
src_region: &Rect,
|
|
||||||
dest_x: i32,
|
|
||||||
dest_y: i32,
|
|
||||||
transparent_color: u8,
|
|
||||||
horizontal_flip: bool,
|
|
||||||
vertical_flip: bool,
|
|
||||||
offset: u8,
|
|
||||||
) {
|
|
||||||
per_pixel_flipped_blit(
|
|
||||||
self, src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip,
|
|
||||||
|src_pixels, dest_pixels| {
|
|
||||||
if *src_pixels != transparent_color {
|
|
||||||
*dest_pixels = (*src_pixels).wrapping_add(offset);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn transparent_single_color_blit(
|
pub unsafe fn transparent_single_color_blit(
|
||||||
&mut self,
|
&mut self,
|
||||||
src: &Bitmap,
|
src: &Self,
|
||||||
src_region: &Rect,
|
src_region: &Rect,
|
||||||
dest_x: i32,
|
dest_x: i32,
|
||||||
dest_y: i32,
|
dest_y: i32,
|
||||||
transparent_color: u8,
|
transparent_color: PixelType,
|
||||||
draw_color: u8,
|
draw_color: PixelType,
|
||||||
) {
|
) {
|
||||||
per_pixel_blit(
|
per_pixel_blit(
|
||||||
self, src, src_region, dest_x, dest_y,
|
self, src, src_region, dest_x, dest_y,
|
||||||
|
@ -648,14 +364,14 @@ impl Bitmap {
|
||||||
|
|
||||||
pub unsafe fn transparent_flipped_single_color_blit(
|
pub unsafe fn transparent_flipped_single_color_blit(
|
||||||
&mut self,
|
&mut self,
|
||||||
src: &Bitmap,
|
src: &Self,
|
||||||
src_region: &Rect,
|
src_region: &Rect,
|
||||||
dest_x: i32,
|
dest_x: i32,
|
||||||
dest_y: i32,
|
dest_y: i32,
|
||||||
transparent_color: u8,
|
transparent_color: PixelType,
|
||||||
horizontal_flip: bool,
|
horizontal_flip: bool,
|
||||||
vertical_flip: bool,
|
vertical_flip: bool,
|
||||||
draw_color: u8,
|
draw_color: PixelType,
|
||||||
) {
|
) {
|
||||||
per_pixel_flipped_blit(
|
per_pixel_flipped_blit(
|
||||||
self, src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip,
|
self, src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip,
|
||||||
|
@ -669,7 +385,7 @@ impl Bitmap {
|
||||||
|
|
||||||
pub unsafe fn rotozoom_blit(
|
pub unsafe fn rotozoom_blit(
|
||||||
&mut self,
|
&mut self,
|
||||||
src: &Bitmap,
|
src: &Self,
|
||||||
src_region: &Rect,
|
src_region: &Rect,
|
||||||
dest_x: i32,
|
dest_x: i32,
|
||||||
dest_y: i32,
|
dest_y: i32,
|
||||||
|
@ -681,47 +397,20 @@ impl Bitmap {
|
||||||
self, src, src_region, dest_x, dest_y, angle, scale_x, scale_y,
|
self, src, src_region, dest_x, dest_y, angle, scale_x, scale_y,
|
||||||
|src_pixel, dest_bitmap, draw_x, draw_y| {
|
|src_pixel, dest_bitmap, draw_x, draw_y| {
|
||||||
dest_bitmap.set_pixel(draw_x, draw_y, src_pixel);
|
dest_bitmap.set_pixel(draw_x, draw_y, src_pixel);
|
||||||
//dest_bitmap.set_pixel(draw_x + 1, draw_y, src_pixel);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn rotozoom_blended_blit(
|
|
||||||
&mut self,
|
|
||||||
src: &Bitmap,
|
|
||||||
src_region: &Rect,
|
|
||||||
dest_x: i32,
|
|
||||||
dest_y: i32,
|
|
||||||
angle: f32,
|
|
||||||
scale_x: f32,
|
|
||||||
scale_y: f32,
|
|
||||||
blend_map: Rc<BlendMap>,
|
|
||||||
) {
|
|
||||||
per_pixel_rotozoom_blit(
|
|
||||||
self, src, src_region, dest_x, dest_y, angle, scale_x, scale_y,
|
|
||||||
|src_pixel, dest_bitmap, draw_x, draw_y| {
|
|
||||||
if let Some(dest_pixel) = dest_bitmap.get_pixel(draw_x, draw_y) {
|
|
||||||
let draw_pixel = if let Some(blended_pixel) = blend_map.blend(src_pixel, dest_pixel) {
|
|
||||||
blended_pixel
|
|
||||||
} else {
|
|
||||||
src_pixel
|
|
||||||
};
|
|
||||||
dest_bitmap.set_pixel(draw_x, draw_y, draw_pixel);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn rotozoom_transparent_blit(
|
pub unsafe fn rotozoom_transparent_blit(
|
||||||
&mut self,
|
&mut self,
|
||||||
src: &Bitmap,
|
src: &Self,
|
||||||
src_region: &Rect,
|
src_region: &Rect,
|
||||||
dest_x: i32,
|
dest_x: i32,
|
||||||
dest_y: i32,
|
dest_y: i32,
|
||||||
angle: f32,
|
angle: f32,
|
||||||
scale_x: f32,
|
scale_x: f32,
|
||||||
scale_y: f32,
|
scale_y: f32,
|
||||||
transparent_color: u8,
|
transparent_color: PixelType,
|
||||||
) {
|
) {
|
||||||
per_pixel_rotozoom_blit(
|
per_pixel_rotozoom_blit(
|
||||||
self, src, src_region, dest_x, dest_y, angle, scale_x, scale_y,
|
self, src, src_region, dest_x, dest_y, angle, scale_x, scale_y,
|
||||||
|
@ -732,241 +421,6 @@ impl Bitmap {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn rotozoom_transparent_blended_blit(
|
|
||||||
&mut self,
|
|
||||||
src: &Bitmap,
|
|
||||||
src_region: &Rect,
|
|
||||||
dest_x: i32,
|
|
||||||
dest_y: i32,
|
|
||||||
angle: f32,
|
|
||||||
scale_x: f32,
|
|
||||||
scale_y: f32,
|
|
||||||
transparent_color: u8,
|
|
||||||
blend_map: Rc<BlendMap>,
|
|
||||||
) {
|
|
||||||
per_pixel_rotozoom_blit(
|
|
||||||
self, src, src_region, dest_x, dest_y, angle, scale_x, scale_y,
|
|
||||||
|src_pixel, dest_bitmap, draw_x, draw_y| {
|
|
||||||
if transparent_color != src_pixel {
|
|
||||||
if let Some(dest_pixel) = dest_bitmap.get_pixel(draw_x, draw_y) {
|
|
||||||
let draw_pixel = if let Some(blended_pixel) = blend_map.blend(src_pixel, dest_pixel) {
|
|
||||||
blended_pixel
|
|
||||||
} else {
|
|
||||||
src_pixel
|
|
||||||
};
|
|
||||||
dest_bitmap.set_pixel(draw_x, draw_y, draw_pixel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn rotozoom_palette_offset_blit(
|
|
||||||
&mut self,
|
|
||||||
src: &Bitmap,
|
|
||||||
src_region: &Rect,
|
|
||||||
dest_x: i32,
|
|
||||||
dest_y: i32,
|
|
||||||
angle: f32,
|
|
||||||
scale_x: f32,
|
|
||||||
scale_y: f32,
|
|
||||||
offset: u8,
|
|
||||||
) {
|
|
||||||
per_pixel_rotozoom_blit(
|
|
||||||
self, src, src_region, dest_x, dest_y, angle, scale_x, scale_y,
|
|
||||||
|src_pixel, dest_bitmap, draw_x, draw_y| {
|
|
||||||
let src_pixel = src_pixel.wrapping_add(offset);
|
|
||||||
dest_bitmap.set_pixel(draw_x, draw_y, src_pixel);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn rotozoom_transparent_palette_offset_blit(
|
|
||||||
&mut self,
|
|
||||||
src: &Bitmap,
|
|
||||||
src_region: &Rect,
|
|
||||||
dest_x: i32,
|
|
||||||
dest_y: i32,
|
|
||||||
angle: f32,
|
|
||||||
scale_x: f32,
|
|
||||||
scale_y: f32,
|
|
||||||
transparent_color: u8,
|
|
||||||
offset: u8,
|
|
||||||
) {
|
|
||||||
per_pixel_rotozoom_blit(
|
|
||||||
self, src, src_region, dest_x, dest_y, angle, scale_x, scale_y,
|
|
||||||
|src_pixel, dest_bitmap, draw_x, draw_y| {
|
|
||||||
if transparent_color != src_pixel {
|
|
||||||
let src_pixel = src_pixel.wrapping_add(offset);
|
|
||||||
dest_bitmap.set_pixel(draw_x, draw_y, src_pixel);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn blit_region(
|
|
||||||
&mut self,
|
|
||||||
method: BlitMethod,
|
|
||||||
src: &Bitmap,
|
|
||||||
src_region: &Rect,
|
|
||||||
mut dest_x: i32,
|
|
||||||
mut dest_y: i32,
|
|
||||||
) {
|
|
||||||
// make sure the source region is clipped or even valid at all for the source bitmap given
|
|
||||||
let mut src_region = *src_region;
|
|
||||||
if !src_region.clamp_to(&src.clip_region) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// some blit methods need to handle clipping a bit differently than others
|
|
||||||
use BlitMethod::*;
|
|
||||||
match method {
|
|
||||||
// rotozoom blits internally clip per-pixel right now ... and regardless, the normal
|
|
||||||
// clip_blit() function wouldn't handle a rotozoom blit destination region anyway ...
|
|
||||||
RotoZoom { .. } => {}
|
|
||||||
RotoZoomBlended { .. } => {}
|
|
||||||
RotoZoomOffset { .. } => {}
|
|
||||||
RotoZoomTransparent { .. } => {}
|
|
||||||
RotoZoomTransparentBlended { .. } => {}
|
|
||||||
RotoZoomTransparentOffset { .. } => {}
|
|
||||||
|
|
||||||
// set axis flip arguments
|
|
||||||
SolidFlipped { horizontal_flip, vertical_flip, .. } |
|
|
||||||
SolidFlippedBlended { horizontal_flip, vertical_flip, .. } |
|
|
||||||
SolidFlippedOffset { horizontal_flip, vertical_flip, .. } |
|
|
||||||
TransparentFlipped { horizontal_flip, vertical_flip, .. } |
|
|
||||||
TransparentFlippedBlended { horizontal_flip, vertical_flip, .. } |
|
|
||||||
TransparentFlippedSingle { horizontal_flip, vertical_flip, .. } |
|
|
||||||
TransparentFlippedOffset { horizontal_flip, vertical_flip, .. } => {
|
|
||||||
if !clip_blit(
|
|
||||||
self.clip_region(),
|
|
||||||
&mut src_region,
|
|
||||||
&mut dest_x,
|
|
||||||
&mut dest_y,
|
|
||||||
horizontal_flip,
|
|
||||||
vertical_flip,
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// otherwise clip like normal!
|
|
||||||
_ => {
|
|
||||||
if !clip_blit(
|
|
||||||
self.clip_region(),
|
|
||||||
&mut src_region,
|
|
||||||
&mut dest_x,
|
|
||||||
&mut dest_y,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
self.blit_region_unchecked(method, src, &src_region, dest_x, dest_y);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
#[rustfmt::skip]
|
|
||||||
pub unsafe fn blit_region_unchecked(
|
|
||||||
&mut self,
|
|
||||||
method: BlitMethod,
|
|
||||||
src: &Bitmap,
|
|
||||||
src_region: &Rect,
|
|
||||||
dest_x: i32,
|
|
||||||
dest_y: i32,
|
|
||||||
) {
|
|
||||||
use BlitMethod::*;
|
|
||||||
match method {
|
|
||||||
Solid => self.solid_blit(src, src_region, dest_x, dest_y),
|
|
||||||
SolidFlipped { horizontal_flip, vertical_flip } => {
|
|
||||||
self.solid_flipped_blit(src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip)
|
|
||||||
}
|
|
||||||
SolidOffset(offset) => self.solid_palette_offset_blit(src, src_region, dest_x, dest_y, offset),
|
|
||||||
SolidFlippedOffset { horizontal_flip, vertical_flip, offset } => {
|
|
||||||
self.solid_flipped_palette_offset_blit(src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip, offset)
|
|
||||||
}
|
|
||||||
Transparent(transparent_color) => {
|
|
||||||
self.transparent_blit(src, src_region, dest_x, dest_y, transparent_color)
|
|
||||||
}
|
|
||||||
TransparentFlipped { transparent_color, horizontal_flip, vertical_flip } => {
|
|
||||||
self.transparent_flipped_blit(src, src_region, dest_x, dest_y, transparent_color, horizontal_flip, vertical_flip)
|
|
||||||
}
|
|
||||||
TransparentOffset { transparent_color, offset } => {
|
|
||||||
self.transparent_palette_offset_blit(src, src_region, dest_x, dest_y, transparent_color, offset)
|
|
||||||
}
|
|
||||||
TransparentFlippedOffset { transparent_color, horizontal_flip, vertical_flip, offset } => {
|
|
||||||
self.transparent_flipped_palette_offset_blit(src, src_region, dest_x, dest_y, transparent_color, horizontal_flip, vertical_flip, offset)
|
|
||||||
}
|
|
||||||
TransparentSingle { transparent_color, draw_color } => {
|
|
||||||
self.transparent_single_color_blit(src, src_region, dest_x, dest_y, transparent_color, draw_color)
|
|
||||||
}
|
|
||||||
TransparentFlippedSingle { transparent_color, horizontal_flip, vertical_flip, draw_color } => {
|
|
||||||
self.transparent_flipped_single_color_blit(src, src_region, dest_x, dest_y, transparent_color, horizontal_flip, vertical_flip, draw_color)
|
|
||||||
}
|
|
||||||
RotoZoom { angle, scale_x, scale_y } => {
|
|
||||||
self.rotozoom_blit(src, src_region, dest_x, dest_y, angle, scale_x, scale_y)
|
|
||||||
}
|
|
||||||
RotoZoomOffset { angle, scale_x, scale_y, offset } => {
|
|
||||||
self.rotozoom_palette_offset_blit(src, src_region, dest_x, dest_y, angle, scale_x, scale_y, offset)
|
|
||||||
}
|
|
||||||
RotoZoomTransparent { angle, scale_x, scale_y, transparent_color } => {
|
|
||||||
self.rotozoom_transparent_blit(src, src_region, dest_x, dest_y, angle, scale_x, scale_y, transparent_color)
|
|
||||||
}
|
|
||||||
RotoZoomTransparentOffset { angle, scale_x, scale_y, transparent_color, offset } => {
|
|
||||||
self.rotozoom_transparent_palette_offset_blit(src, src_region, dest_x, dest_y, angle, scale_x, scale_y, transparent_color, offset)
|
|
||||||
}
|
|
||||||
SolidBlended { blend_map } => {
|
|
||||||
self.solid_blended_blit(src, src_region, dest_x, dest_y, blend_map)
|
|
||||||
}
|
|
||||||
SolidFlippedBlended { horizontal_flip, vertical_flip, blend_map } => {
|
|
||||||
self.solid_flipped_blended_blit(src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip, blend_map)
|
|
||||||
}
|
|
||||||
TransparentBlended { transparent_color, blend_map } => {
|
|
||||||
self.transparent_blended_blit(src, src_region, dest_x, dest_y, transparent_color, blend_map)
|
|
||||||
}
|
|
||||||
TransparentFlippedBlended { transparent_color, horizontal_flip, vertical_flip, blend_map } => {
|
|
||||||
self.transparent_flipped_blended_blit(src, src_region, dest_x, dest_y, transparent_color, horizontal_flip, vertical_flip, blend_map)
|
|
||||||
}
|
|
||||||
RotoZoomBlended { angle, scale_x, scale_y, blend_map } => {
|
|
||||||
self.rotozoom_blended_blit(src, src_region, dest_x, dest_y, angle, scale_x, scale_y, blend_map)
|
|
||||||
}
|
|
||||||
RotoZoomTransparentBlended { angle, scale_x, scale_y, transparent_color, blend_map } => {
|
|
||||||
self.rotozoom_transparent_blended_blit(src, src_region, dest_x, dest_y, angle, scale_x, scale_y, transparent_color, blend_map)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn blit(&mut self, method: BlitMethod, src: &Bitmap, x: i32, y: i32) {
|
|
||||||
let src_region = Rect::new(0, 0, src.width, src.height);
|
|
||||||
self.blit_region(method, src, &src_region, x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn blit_atlas(&mut self, method: BlitMethod, src: &BitmapAtlas<Self>, index: usize, x: i32, y: i32) {
|
|
||||||
if let Some(src_region) = src.get(index) {
|
|
||||||
self.blit_region(method, src.bitmap(), src_region, x, y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub unsafe fn blit_unchecked(&mut self, method: BlitMethod, src: &Bitmap, x: i32, y: i32) {
|
|
||||||
let src_region = Rect::new(0, 0, src.width, src.height);
|
|
||||||
self.blit_region_unchecked(method, src, &src_region, x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub unsafe fn blit_atlas_unchecked(&mut self, method: BlitMethod, src: &BitmapAtlas<Self>, index: usize, x: i32, y: i32) {
|
|
||||||
if let Some(src_region) = src.get(index) {
|
|
||||||
self.blit_region_unchecked(method, src.bitmap(), &src_region, x, y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
|
@ -6,9 +6,12 @@
|
||||||
//!
|
//!
|
||||||
//! Only a subset of the most common Bitmap drawing operations will be provided here.
|
//! Only a subset of the most common Bitmap drawing operations will be provided here.
|
||||||
|
|
||||||
use std::error::Error;
|
use crate::graphics::bitmap::BitmapError;
|
||||||
|
use crate::graphics::bitmap::indexed::blit::IndexedBlitMethod;
|
||||||
use crate::graphics::{indexed, Pixel};
|
use crate::graphics::bitmap::indexed::IndexedBitmap;
|
||||||
|
use crate::graphics::bitmap::rgb::blit::RgbaBlitMethod;
|
||||||
|
use crate::graphics::bitmap::rgb::RgbaBitmap;
|
||||||
|
use crate::graphics::Pixel;
|
||||||
use crate::math::rect::Rect;
|
use crate::math::rect::Rect;
|
||||||
|
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
|
@ -22,10 +25,9 @@ pub enum GeneralBlitMethod<PixelType: Pixel> {
|
||||||
/// any one pixel-depth. Note that this does not provide cross-bit-depth drawing support.
|
/// any one pixel-depth. Note that this does not provide cross-bit-depth drawing support.
|
||||||
pub trait GeneralBitmap: Sized + Clone {
|
pub trait GeneralBitmap: Sized + Clone {
|
||||||
type PixelType: Pixel;
|
type PixelType: Pixel;
|
||||||
type ErrorType: Error;
|
|
||||||
|
|
||||||
/// Creates a new bitmap with the specified dimensions, in pixels.
|
/// Creates a new bitmap with the specified dimensions, in pixels.
|
||||||
fn new(width: u32, height: u32) -> Result<Self, Self::ErrorType>;
|
fn new(width: u32, height: u32) -> Result<Self, BitmapError>;
|
||||||
|
|
||||||
/// Returns the width of the bitmap in pixels.
|
/// Returns the width of the bitmap in pixels.
|
||||||
fn width(&self) -> u32;
|
fn width(&self) -> u32;
|
||||||
|
@ -94,20 +96,17 @@ pub trait GeneralBitmap: Sized + Clone {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GeneralBitmap for indexed::bitmap::Bitmap {
|
impl GeneralBitmap for IndexedBitmap {
|
||||||
type PixelType = u8;
|
type PixelType = u8;
|
||||||
type ErrorType = indexed::bitmap::BitmapError;
|
|
||||||
|
|
||||||
fn new(width: u32, height: u32) -> Result<Self, Self::ErrorType> {
|
fn new(width: u32, height: u32) -> Result<Self, BitmapError> {
|
||||||
Self::new(width, height)
|
Self::new(width, height)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn width(&self) -> u32 {
|
fn width(&self) -> u32 {
|
||||||
self.width()
|
self.width()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn height(&self) -> u32 {
|
fn height(&self) -> u32 {
|
||||||
self.height()
|
self.height()
|
||||||
}
|
}
|
||||||
|
@ -116,59 +115,48 @@ impl GeneralBitmap for indexed::bitmap::Bitmap {
|
||||||
self.clip_region()
|
self.clip_region()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn full_bounds(&self) -> Rect {
|
fn full_bounds(&self) -> Rect {
|
||||||
self.full_bounds()
|
self.full_bounds()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
fn clear(&mut self, color: Self::PixelType) {
|
||||||
fn clear(&mut self, color: u8) {
|
self.clear(color)
|
||||||
self.clear(color);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
fn set_pixel(&mut self, x: i32, y: i32, color: Self::PixelType) {
|
||||||
fn set_pixel(&mut self, x: i32, y: i32, color: u8) {
|
self.set_pixel(x, y, color)
|
||||||
self.set_pixel(x, y, color);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
fn get_pixel(&self, x: i32, y: i32) -> Option<Self::PixelType> {
|
||||||
fn get_pixel(&self, x: i32, y: i32) -> Option<u8> {
|
|
||||||
self.get_pixel(x, y)
|
self.get_pixel(x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
fn line(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: Self::PixelType) {
|
||||||
fn line(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: u8) {
|
self.line(x1, y1, x2, y2, color)
|
||||||
self.line(x1, y1, x2, y2, color);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
fn horiz_line(&mut self, x1: i32, x2: i32, y: i32, color: Self::PixelType) {
|
||||||
fn horiz_line(&mut self, x1: i32, x2: i32, y: i32, color: u8) {
|
self.horiz_line(x1, x2, y, color)
|
||||||
self.horiz_line(x1, x2, y, color);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
fn vert_line(&mut self, x: i32, y1: i32, y2: i32, color: Self::PixelType) {
|
||||||
fn vert_line(&mut self, x: i32, y1: i32, y2: i32, color: u8) {
|
self.vert_line(x, y1, y2, color)
|
||||||
self.vert_line(x, y1, y2, color);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
fn rect(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: Self::PixelType) {
|
||||||
fn rect(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: u8) {
|
self.rect(x1, y1, x2, y2, color)
|
||||||
self.rect(x1, y1, x2, y2, color);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
fn filled_rect(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: Self::PixelType) {
|
||||||
fn filled_rect(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: u8) {
|
self.filled_rect(x1, y1, x2, y2, color)
|
||||||
self.filled_rect(x1, y1, x2, y2, color);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
fn circle(&mut self, center_x: i32, center_y: i32, radius: u32, color: Self::PixelType) {
|
||||||
fn circle(&mut self, center_x: i32, center_y: i32, radius: u32, color: u8) {
|
self.circle(center_x, center_y, radius, color)
|
||||||
self.circle(center_x, center_y, radius, color);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
fn filled_circle(&mut self, center_x: i32, center_y: i32, radius: u32, color: Self::PixelType) {
|
||||||
fn filled_circle(&mut self, center_x: i32, center_y: i32, radius: u32, color: u8) {
|
self.filled_circle(center_x, center_y, radius, color)
|
||||||
self.filled_circle(center_x, center_y, radius, color);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn blit_region(
|
fn blit_region(
|
||||||
|
@ -179,11 +167,88 @@ impl GeneralBitmap for indexed::bitmap::Bitmap {
|
||||||
dest_x: i32,
|
dest_x: i32,
|
||||||
dest_y: i32
|
dest_y: i32
|
||||||
) {
|
) {
|
||||||
use indexed::bitmap::blit::BlitMethod;
|
|
||||||
|
|
||||||
let blit_method = match method {
|
let blit_method = match method {
|
||||||
GeneralBlitMethod::Solid => BlitMethod::Solid,
|
GeneralBlitMethod::Solid => IndexedBlitMethod::Solid,
|
||||||
GeneralBlitMethod::Transparent(color) => BlitMethod::Transparent(color),
|
GeneralBlitMethod::Transparent(color) => IndexedBlitMethod::Transparent(color),
|
||||||
|
};
|
||||||
|
self.blit_region(blit_method, src, src_region, dest_x, dest_y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GeneralBitmap for RgbaBitmap {
|
||||||
|
type PixelType = u32;
|
||||||
|
|
||||||
|
fn new(width: u32, height: u32) -> Result<Self, BitmapError> {
|
||||||
|
Self::new(width, height)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn width(&self) -> u32 {
|
||||||
|
self.width()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn height(&self) -> u32 {
|
||||||
|
self.height()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clip_region(&self) -> &Rect {
|
||||||
|
self.clip_region()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn full_bounds(&self) -> Rect {
|
||||||
|
self.full_bounds()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear(&mut self, color: Self::PixelType) {
|
||||||
|
self.clear(color)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_pixel(&mut self, x: i32, y: i32, color: Self::PixelType) {
|
||||||
|
self.set_pixel(x, y, color)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_pixel(&self, x: i32, y: i32) -> Option<Self::PixelType> {
|
||||||
|
self.get_pixel(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn line(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: Self::PixelType) {
|
||||||
|
self.line(x1, y1, x2, y2, color)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn horiz_line(&mut self, x1: i32, x2: i32, y: i32, color: Self::PixelType) {
|
||||||
|
self.horiz_line(x1, x2, y, color)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vert_line(&mut self, x: i32, y1: i32, y2: i32, color: Self::PixelType) {
|
||||||
|
self.vert_line(x, y1, y2, color)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rect(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: Self::PixelType) {
|
||||||
|
self.rect(x1, y1, x2, y2, color)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn filled_rect(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: Self::PixelType) {
|
||||||
|
self.filled_rect(x1, y1, x2, y2, color)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn circle(&mut self, center_x: i32, center_y: i32, radius: u32, color: Self::PixelType) {
|
||||||
|
self.circle(center_x, center_y, radius, color)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn filled_circle(&mut self, center_x: i32, center_y: i32, radius: u32, color: Self::PixelType) {
|
||||||
|
self.filled_circle(center_x, center_y, radius, color)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn blit_region(
|
||||||
|
&mut self,
|
||||||
|
method: GeneralBlitMethod<Self::PixelType>,
|
||||||
|
src: &Self,
|
||||||
|
src_region: &Rect,
|
||||||
|
dest_x: i32,
|
||||||
|
dest_y: i32
|
||||||
|
) {
|
||||||
|
let blit_method = match method {
|
||||||
|
GeneralBlitMethod::Solid => RgbaBlitMethod::Solid,
|
||||||
|
GeneralBlitMethod::Transparent(color) => RgbaBlitMethod::Transparent(color),
|
||||||
};
|
};
|
||||||
self.blit_region(blit_method, src, src_region, dest_x, dest_y)
|
self.blit_region(blit_method, src, src_region, dest_x, dest_y)
|
||||||
}
|
}
|
|
@ -5,8 +5,8 @@ use std::path::Path;
|
||||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::graphics::indexed::bitmap::Bitmap;
|
use crate::graphics::bitmap::indexed::IndexedBitmap;
|
||||||
use crate::graphics::indexed::palette::{Palette, PaletteError, PaletteFormat};
|
use crate::graphics::palette::{Palette, PaletteError, PaletteFormat};
|
||||||
use crate::utils::lzwgif::{lzw_decode, lzw_encode, LzwError};
|
use crate::utils::lzwgif::{lzw_decode, lzw_encode, LzwError};
|
||||||
|
|
||||||
const BITS_FOR_256_COLORS: u32 = 7; // formula is `2 ^ (bits + 1) = num_colors`
|
const BITS_FOR_256_COLORS: u32 = 7; // formula is `2 ^ (bits + 1) = num_colors`
|
||||||
|
@ -352,7 +352,7 @@ fn load_image_section<T: ReadBytesExt>(
|
||||||
reader: &mut T,
|
reader: &mut T,
|
||||||
gif_header: &GifHeader,
|
gif_header: &GifHeader,
|
||||||
_graphic_control: &Option<GraphicControlExtension>,
|
_graphic_control: &Option<GraphicControlExtension>,
|
||||||
) -> Result<(Bitmap, Option<Palette>), GifError> {
|
) -> Result<(IndexedBitmap, Option<Palette>), GifError> {
|
||||||
let descriptor = LocalImageDescriptor::read(reader)?;
|
let descriptor = LocalImageDescriptor::read(reader)?;
|
||||||
|
|
||||||
let palette: Option<Palette>;
|
let palette: Option<Palette>;
|
||||||
|
@ -367,7 +367,7 @@ fn load_image_section<T: ReadBytesExt>(
|
||||||
palette = None; // we expect that there was a global color table previously
|
palette = None; // we expect that there was a global color table previously
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut bitmap = Bitmap::new(gif_header.screen_width as u32, gif_header.screen_height as u32).unwrap();
|
let mut bitmap = IndexedBitmap::new(gif_header.screen_width as u32, gif_header.screen_height as u32).unwrap();
|
||||||
let mut writer = bitmap.pixels_mut();
|
let mut writer = bitmap.pixels_mut();
|
||||||
lzw_decode(reader, &mut writer)?;
|
lzw_decode(reader, &mut writer)?;
|
||||||
|
|
||||||
|
@ -376,7 +376,7 @@ fn load_image_section<T: ReadBytesExt>(
|
||||||
|
|
||||||
fn save_image_section<T: WriteBytesExt>(
|
fn save_image_section<T: WriteBytesExt>(
|
||||||
writer: &mut T,
|
writer: &mut T,
|
||||||
bitmap: &Bitmap,
|
bitmap: &IndexedBitmap,
|
||||||
) -> Result<(), GifError> {
|
) -> Result<(), GifError> {
|
||||||
writer.write_u8(IMAGE_DESCRIPTOR_SEPARATOR)?;
|
writer.write_u8(IMAGE_DESCRIPTOR_SEPARATOR)?;
|
||||||
let image_descriptor = LocalImageDescriptor {
|
let image_descriptor = LocalImageDescriptor {
|
||||||
|
@ -398,10 +398,10 @@ fn save_image_section<T: WriteBytesExt>(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Bitmap {
|
impl IndexedBitmap {
|
||||||
pub fn load_gif_bytes<T: ReadBytesExt>(
|
pub fn load_gif_bytes<T: ReadBytesExt>(
|
||||||
reader: &mut T,
|
reader: &mut T,
|
||||||
) -> Result<(Bitmap, Palette), GifError> {
|
) -> Result<(IndexedBitmap, Palette), GifError> {
|
||||||
let header = GifHeader::read(reader)?;
|
let header = GifHeader::read(reader)?;
|
||||||
if header.signature != *b"GIF" || header.version != *b"89a" {
|
if header.signature != *b"GIF" || header.version != *b"89a" {
|
||||||
return Err(GifError::BadFile(String::from("Expected GIF89a header signature")));
|
return Err(GifError::BadFile(String::from("Expected GIF89a header signature")));
|
||||||
|
@ -420,7 +420,7 @@ impl Bitmap {
|
||||||
palette = None; // we expect to find a local color table later
|
palette = None; // we expect to find a local color table later
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut bitmap: Option<Bitmap> = None;
|
let mut bitmap: Option<IndexedBitmap> = None;
|
||||||
let mut current_graphic_control: Option<GraphicControlExtension> = None;
|
let mut current_graphic_control: Option<GraphicControlExtension> = None;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
@ -482,7 +482,7 @@ impl Bitmap {
|
||||||
Ok((bitmap.unwrap(), palette.unwrap()))
|
Ok((bitmap.unwrap(), palette.unwrap()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_gif_file(path: &Path) -> Result<(Bitmap, Palette), GifError> {
|
pub fn load_gif_file(path: &Path) -> Result<(IndexedBitmap, Palette), GifError> {
|
||||||
let f = File::open(path)?;
|
let f = File::open(path)?;
|
||||||
let mut reader = BufReader::new(f);
|
let mut reader = BufReader::new(f);
|
||||||
Self::load_gif_bytes(&mut reader)
|
Self::load_gif_bytes(&mut reader)
|
||||||
|
@ -557,9 +557,9 @@ pub mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub static TEST_BMP_PIXELS_RAW: &[u8] = include_bytes!("../../../../test-assets/test_bmp_pixels_raw.bin");
|
pub static TEST_BMP_PIXELS_RAW: &[u8] = include_bytes!("../../../test-assets/test_bmp_pixels_raw.bin");
|
||||||
pub static TEST_LARGE_BMP_PIXELS_RAW: &[u8] = include_bytes!("../../../../test-assets/test_large_bmp_pixels_raw.bin");
|
pub static TEST_LARGE_BMP_PIXELS_RAW: &[u8] = include_bytes!("../../../test-assets/test_large_bmp_pixels_raw.bin");
|
||||||
pub static TEST_LARGE_BMP_PIXELS_RAW_2: &[u8] = include_bytes!("../../../../test-assets/test_large_bmp_pixels_raw2.bin");
|
pub static TEST_LARGE_BMP_PIXELS_RAW_2: &[u8] = include_bytes!("../../../test-assets/test_large_bmp_pixels_raw2.bin");
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn load_and_save() -> Result<(), GifError> {
|
fn load_and_save() -> Result<(), GifError> {
|
||||||
|
@ -568,7 +568,7 @@ pub mod tests {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let tmp_dir = TempDir::new()?;
|
let tmp_dir = TempDir::new()?;
|
||||||
|
|
||||||
let (bmp, palette) = Bitmap::load_gif_file(Path::new("./test-assets/test.gif"))?;
|
let (bmp, palette) = IndexedBitmap::load_gif_file(Path::new("./test-assets/test.gif"))?;
|
||||||
assert_eq!(16, bmp.width());
|
assert_eq!(16, bmp.width());
|
||||||
assert_eq!(16, bmp.height());
|
assert_eq!(16, bmp.height());
|
||||||
assert_eq!(bmp.pixels(), TEST_BMP_PIXELS_RAW);
|
assert_eq!(bmp.pixels(), TEST_BMP_PIXELS_RAW);
|
||||||
|
@ -576,7 +576,7 @@ pub mod tests {
|
||||||
|
|
||||||
let save_path = tmp_dir.path().join("test_save.gif");
|
let save_path = tmp_dir.path().join("test_save.gif");
|
||||||
bmp.to_gif_file(&save_path, &palette, GifSettings::Default)?;
|
bmp.to_gif_file(&save_path, &palette, GifSettings::Default)?;
|
||||||
let (reloaded_bmp, reloaded_palette) = Bitmap::load_gif_file(&save_path)?;
|
let (reloaded_bmp, reloaded_palette) = IndexedBitmap::load_gif_file(&save_path)?;
|
||||||
assert_eq!(16, reloaded_bmp.width());
|
assert_eq!(16, reloaded_bmp.width());
|
||||||
assert_eq!(16, reloaded_bmp.height());
|
assert_eq!(16, reloaded_bmp.height());
|
||||||
assert_eq!(reloaded_bmp.pixels(), TEST_BMP_PIXELS_RAW);
|
assert_eq!(reloaded_bmp.pixels(), TEST_BMP_PIXELS_RAW);
|
||||||
|
@ -594,28 +594,28 @@ pub mod tests {
|
||||||
|
|
||||||
// first image
|
// first image
|
||||||
|
|
||||||
let (bmp, palette) = Bitmap::load_gif_file(Path::new("./test-assets/test_image.gif"))?;
|
let (bmp, palette) = IndexedBitmap::load_gif_file(Path::new("./test-assets/test_image.gif"))?;
|
||||||
assert_eq!(320, bmp.width());
|
assert_eq!(320, bmp.width());
|
||||||
assert_eq!(200, bmp.height());
|
assert_eq!(200, bmp.height());
|
||||||
assert_eq!(bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW);
|
assert_eq!(bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW);
|
||||||
|
|
||||||
let save_path = tmp_dir.path().join("test_save.gif");
|
let save_path = tmp_dir.path().join("test_save.gif");
|
||||||
bmp.to_gif_file(&save_path, &palette, GifSettings::Default)?;
|
bmp.to_gif_file(&save_path, &palette, GifSettings::Default)?;
|
||||||
let (reloaded_bmp, _) = Bitmap::load_gif_file(&save_path)?;
|
let (reloaded_bmp, _) = IndexedBitmap::load_gif_file(&save_path)?;
|
||||||
assert_eq!(320, reloaded_bmp.width());
|
assert_eq!(320, reloaded_bmp.width());
|
||||||
assert_eq!(200, reloaded_bmp.height());
|
assert_eq!(200, reloaded_bmp.height());
|
||||||
assert_eq!(reloaded_bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW);
|
assert_eq!(reloaded_bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW);
|
||||||
|
|
||||||
// second image
|
// second image
|
||||||
|
|
||||||
let (bmp, palette) = Bitmap::load_gif_file(Path::new("./test-assets/test_image2.gif"))?;
|
let (bmp, palette) = IndexedBitmap::load_gif_file(Path::new("./test-assets/test_image2.gif"))?;
|
||||||
assert_eq!(320, bmp.width());
|
assert_eq!(320, bmp.width());
|
||||||
assert_eq!(200, bmp.height());
|
assert_eq!(200, bmp.height());
|
||||||
assert_eq!(bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW_2);
|
assert_eq!(bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW_2);
|
||||||
|
|
||||||
let save_path = tmp_dir.path().join("test_save_2.gif");
|
let save_path = tmp_dir.path().join("test_save_2.gif");
|
||||||
bmp.to_gif_file(&save_path, &palette, GifSettings::Default)?;
|
bmp.to_gif_file(&save_path, &palette, GifSettings::Default)?;
|
||||||
let (reloaded_bmp, _) = Bitmap::load_gif_file(&save_path)?;
|
let (reloaded_bmp, _) = IndexedBitmap::load_gif_file(&save_path)?;
|
||||||
assert_eq!(320, reloaded_bmp.width());
|
assert_eq!(320, reloaded_bmp.width());
|
||||||
assert_eq!(200, reloaded_bmp.height());
|
assert_eq!(200, reloaded_bmp.height());
|
||||||
assert_eq!(reloaded_bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW_2);
|
assert_eq!(reloaded_bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW_2);
|
|
@ -6,8 +6,8 @@ use std::path::Path;
|
||||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::graphics::indexed::bitmap::Bitmap;
|
use crate::graphics::bitmap::indexed::IndexedBitmap;
|
||||||
use crate::graphics::indexed::palette::{Palette, PaletteError, PaletteFormat};
|
use crate::graphics::palette::{Palette, PaletteError, PaletteFormat};
|
||||||
use crate::utils::packbits::{pack_bits, PackBitsError, unpack_bits};
|
use crate::utils::packbits::{pack_bits, PackBitsError, unpack_bits};
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
|
@ -250,8 +250,8 @@ fn extract_bitplane(plane: u32, src: &[u8], dest: &mut [u8], row_size: usize) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_planar_body<T: ReadBytesExt>(reader: &mut T, bmhd: &BMHDChunk) -> Result<Bitmap, IffError> {
|
fn load_planar_body<T: ReadBytesExt>(reader: &mut T, bmhd: &BMHDChunk) -> Result<IndexedBitmap, IffError> {
|
||||||
let mut bitmap = Bitmap::new(bmhd.width as u32, bmhd.height as u32).unwrap();
|
let mut bitmap = IndexedBitmap::new(bmhd.width as u32, bmhd.height as u32).unwrap();
|
||||||
|
|
||||||
let row_bytes = (((bmhd.width + 15) >> 4) << 1) as usize;
|
let row_bytes = (((bmhd.width + 15) >> 4) << 1) as usize;
|
||||||
let mut buffer = vec![0u8; row_bytes];
|
let mut buffer = vec![0u8; row_bytes];
|
||||||
|
@ -297,8 +297,8 @@ fn load_planar_body<T: ReadBytesExt>(reader: &mut T, bmhd: &BMHDChunk) -> Result
|
||||||
Ok(bitmap)
|
Ok(bitmap)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_chunky_body<T: ReadBytesExt>(reader: &mut T, bmhd: &BMHDChunk) -> Result<Bitmap, IffError> {
|
fn load_chunky_body<T: ReadBytesExt>(reader: &mut T, bmhd: &BMHDChunk) -> Result<IndexedBitmap, IffError> {
|
||||||
let mut bitmap = Bitmap::new(bmhd.width as u32, bmhd.height as u32).unwrap();
|
let mut bitmap = IndexedBitmap::new(bmhd.width as u32, bmhd.height as u32).unwrap();
|
||||||
|
|
||||||
for y in 0..bmhd.height {
|
for y in 0..bmhd.height {
|
||||||
if bmhd.compress == 1 {
|
if bmhd.compress == 1 {
|
||||||
|
@ -317,7 +317,7 @@ fn load_chunky_body<T: ReadBytesExt>(reader: &mut T, bmhd: &BMHDChunk) -> Result
|
||||||
|
|
||||||
fn write_planar_body<T: WriteBytesExt>(
|
fn write_planar_body<T: WriteBytesExt>(
|
||||||
writer: &mut T,
|
writer: &mut T,
|
||||||
bitmap: &Bitmap,
|
bitmap: &IndexedBitmap,
|
||||||
bmhd: &BMHDChunk,
|
bmhd: &BMHDChunk,
|
||||||
) -> Result<(), IffError> {
|
) -> Result<(), IffError> {
|
||||||
let row_bytes = (((bitmap.width() + 15) >> 4) << 1) as usize;
|
let row_bytes = (((bitmap.width() + 15) >> 4) << 1) as usize;
|
||||||
|
@ -349,7 +349,7 @@ fn write_planar_body<T: WriteBytesExt>(
|
||||||
|
|
||||||
fn write_chunky_body<T: WriteBytesExt>(
|
fn write_chunky_body<T: WriteBytesExt>(
|
||||||
writer: &mut T,
|
writer: &mut T,
|
||||||
bitmap: &Bitmap,
|
bitmap: &IndexedBitmap,
|
||||||
bmhd: &BMHDChunk,
|
bmhd: &BMHDChunk,
|
||||||
) -> Result<(), IffError> {
|
) -> Result<(), IffError> {
|
||||||
for y in 0..bitmap.height() {
|
for y in 0..bitmap.height() {
|
||||||
|
@ -367,10 +367,10 @@ fn write_chunky_body<T: WriteBytesExt>(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Bitmap {
|
impl IndexedBitmap {
|
||||||
pub fn load_iff_bytes<T: ReadBytesExt + Seek>(
|
pub fn load_iff_bytes<T: ReadBytesExt + Seek>(
|
||||||
reader: &mut T,
|
reader: &mut T,
|
||||||
) -> Result<(Bitmap, Palette), IffError> {
|
) -> Result<(IndexedBitmap, Palette), IffError> {
|
||||||
let form_chunk = FormChunkHeader::read(reader)?;
|
let form_chunk = FormChunkHeader::read(reader)?;
|
||||||
if form_chunk.chunk_id.id != *b"FORM" {
|
if form_chunk.chunk_id.id != *b"FORM" {
|
||||||
return Err(IffError::BadFile(String::from(
|
return Err(IffError::BadFile(String::from(
|
||||||
|
@ -385,7 +385,7 @@ impl Bitmap {
|
||||||
|
|
||||||
let mut bmhd: Option<BMHDChunk> = None;
|
let mut bmhd: Option<BMHDChunk> = None;
|
||||||
let mut palette: Option<Palette> = None;
|
let mut palette: Option<Palette> = None;
|
||||||
let mut bitmap: Option<Bitmap> = None;
|
let mut bitmap: Option<IndexedBitmap> = None;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let header = match SubChunkHeader::read(reader) {
|
let header = match SubChunkHeader::read(reader) {
|
||||||
|
@ -447,7 +447,7 @@ impl Bitmap {
|
||||||
Ok((bitmap.unwrap(), palette.unwrap()))
|
Ok((bitmap.unwrap(), palette.unwrap()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_iff_file(path: &Path) -> Result<(Bitmap, Palette), IffError> {
|
pub fn load_iff_file(path: &Path) -> Result<(IndexedBitmap, Palette), IffError> {
|
||||||
let f = File::open(path)?;
|
let f = File::open(path)?;
|
||||||
let mut reader = BufReader::new(f);
|
let mut reader = BufReader::new(f);
|
||||||
Self::load_iff_bytes(&mut reader)
|
Self::load_iff_bytes(&mut reader)
|
||||||
|
@ -565,9 +565,9 @@ mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub static TEST_BMP_PIXELS_RAW: &[u8] = include_bytes!("../../../../test-assets/test_bmp_pixels_raw.bin");
|
pub static TEST_BMP_PIXELS_RAW: &[u8] = include_bytes!("../../../test-assets/test_bmp_pixels_raw.bin");
|
||||||
pub static TEST_LARGE_BMP_PIXELS_RAW: &[u8] = include_bytes!("../../../../test-assets/test_large_bmp_pixels_raw.bin");
|
pub static TEST_LARGE_BMP_PIXELS_RAW: &[u8] = include_bytes!("../../../test-assets/test_large_bmp_pixels_raw.bin");
|
||||||
pub static TEST_LARGE_BMP_PIXELS_RAW_2: &[u8] = include_bytes!("../../../../test-assets/test_large_bmp_pixels_raw2.bin");
|
pub static TEST_LARGE_BMP_PIXELS_RAW_2: &[u8] = include_bytes!("../../../test-assets/test_large_bmp_pixels_raw2.bin");
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn load_and_save() -> Result<(), IffError> {
|
pub fn load_and_save() -> Result<(), IffError> {
|
||||||
|
@ -578,7 +578,7 @@ mod tests {
|
||||||
|
|
||||||
// ILBM format
|
// ILBM format
|
||||||
|
|
||||||
let (bmp, palette) = Bitmap::load_iff_file(Path::new("./test-assets/test_ilbm.lbm"))?;
|
let (bmp, palette) = IndexedBitmap::load_iff_file(Path::new("./test-assets/test_ilbm.lbm"))?;
|
||||||
assert_eq!(16, bmp.width());
|
assert_eq!(16, bmp.width());
|
||||||
assert_eq!(16, bmp.height());
|
assert_eq!(16, bmp.height());
|
||||||
assert_eq!(bmp.pixels(), TEST_BMP_PIXELS_RAW);
|
assert_eq!(bmp.pixels(), TEST_BMP_PIXELS_RAW);
|
||||||
|
@ -586,7 +586,7 @@ mod tests {
|
||||||
|
|
||||||
let save_path = tmp_dir.path().join("test_save_ilbm.lbm");
|
let save_path = tmp_dir.path().join("test_save_ilbm.lbm");
|
||||||
bmp.to_iff_file(&save_path, &palette, IffFormat::Ilbm)?;
|
bmp.to_iff_file(&save_path, &palette, IffFormat::Ilbm)?;
|
||||||
let (reloaded_bmp, reloaded_palette) = Bitmap::load_iff_file(&save_path)?;
|
let (reloaded_bmp, reloaded_palette) = IndexedBitmap::load_iff_file(&save_path)?;
|
||||||
assert_eq!(16, reloaded_bmp.width());
|
assert_eq!(16, reloaded_bmp.width());
|
||||||
assert_eq!(16, reloaded_bmp.height());
|
assert_eq!(16, reloaded_bmp.height());
|
||||||
assert_eq!(reloaded_bmp.pixels(), TEST_BMP_PIXELS_RAW);
|
assert_eq!(reloaded_bmp.pixels(), TEST_BMP_PIXELS_RAW);
|
||||||
|
@ -594,7 +594,7 @@ mod tests {
|
||||||
|
|
||||||
// PBM format
|
// PBM format
|
||||||
|
|
||||||
let (bmp, palette) = Bitmap::load_iff_file(Path::new("./test-assets/test_pbm.lbm"))?;
|
let (bmp, palette) = IndexedBitmap::load_iff_file(Path::new("./test-assets/test_pbm.lbm"))?;
|
||||||
assert_eq!(16, bmp.width());
|
assert_eq!(16, bmp.width());
|
||||||
assert_eq!(16, bmp.height());
|
assert_eq!(16, bmp.height());
|
||||||
assert_eq!(bmp.pixels(), TEST_BMP_PIXELS_RAW);
|
assert_eq!(bmp.pixels(), TEST_BMP_PIXELS_RAW);
|
||||||
|
@ -602,7 +602,7 @@ mod tests {
|
||||||
|
|
||||||
let save_path = tmp_dir.path().join("test_save_pbm.lbm");
|
let save_path = tmp_dir.path().join("test_save_pbm.lbm");
|
||||||
bmp.to_iff_file(&save_path, &palette, IffFormat::Pbm)?;
|
bmp.to_iff_file(&save_path, &palette, IffFormat::Pbm)?;
|
||||||
let (reloaded_bmp, reloaded_palette) = Bitmap::load_iff_file(&save_path)?;
|
let (reloaded_bmp, reloaded_palette) = IndexedBitmap::load_iff_file(&save_path)?;
|
||||||
assert_eq!(16, reloaded_bmp.width());
|
assert_eq!(16, reloaded_bmp.width());
|
||||||
assert_eq!(16, reloaded_bmp.height());
|
assert_eq!(16, reloaded_bmp.height());
|
||||||
assert_eq!(reloaded_bmp.pixels(), TEST_BMP_PIXELS_RAW);
|
assert_eq!(reloaded_bmp.pixels(), TEST_BMP_PIXELS_RAW);
|
||||||
|
@ -617,56 +617,56 @@ mod tests {
|
||||||
|
|
||||||
// first image, PBM format
|
// first image, PBM format
|
||||||
|
|
||||||
let (bmp, palette) = Bitmap::load_iff_file(Path::new("./test-assets/test_pbm_image.lbm"))?;
|
let (bmp, palette) = IndexedBitmap::load_iff_file(Path::new("./test-assets/test_pbm_image.lbm"))?;
|
||||||
assert_eq!(320, bmp.width());
|
assert_eq!(320, bmp.width());
|
||||||
assert_eq!(200, bmp.height());
|
assert_eq!(200, bmp.height());
|
||||||
assert_eq!(bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW);
|
assert_eq!(bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW);
|
||||||
|
|
||||||
let save_path = tmp_dir.path().join("test_save_pbm_image.lbm");
|
let save_path = tmp_dir.path().join("test_save_pbm_image.lbm");
|
||||||
bmp.to_iff_file(&save_path, &palette, IffFormat::Pbm)?;
|
bmp.to_iff_file(&save_path, &palette, IffFormat::Pbm)?;
|
||||||
let (reloaded_bmp, _) = Bitmap::load_iff_file(&save_path)?;
|
let (reloaded_bmp, _) = IndexedBitmap::load_iff_file(&save_path)?;
|
||||||
assert_eq!(320, reloaded_bmp.width());
|
assert_eq!(320, reloaded_bmp.width());
|
||||||
assert_eq!(200, reloaded_bmp.height());
|
assert_eq!(200, reloaded_bmp.height());
|
||||||
assert_eq!(reloaded_bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW);
|
assert_eq!(reloaded_bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW);
|
||||||
|
|
||||||
// second image, PBM format
|
// second image, PBM format
|
||||||
|
|
||||||
let (bmp, palette) = Bitmap::load_iff_file(Path::new("./test-assets/test_pbm_image2.lbm"))?;
|
let (bmp, palette) = IndexedBitmap::load_iff_file(Path::new("./test-assets/test_pbm_image2.lbm"))?;
|
||||||
assert_eq!(320, bmp.width());
|
assert_eq!(320, bmp.width());
|
||||||
assert_eq!(200, bmp.height());
|
assert_eq!(200, bmp.height());
|
||||||
assert_eq!(bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW_2);
|
assert_eq!(bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW_2);
|
||||||
|
|
||||||
let save_path = tmp_dir.path().join("test_save_pbm_image2.lbm");
|
let save_path = tmp_dir.path().join("test_save_pbm_image2.lbm");
|
||||||
bmp.to_iff_file(&save_path, &palette, IffFormat::Pbm)?;
|
bmp.to_iff_file(&save_path, &palette, IffFormat::Pbm)?;
|
||||||
let (reloaded_bmp, _) = Bitmap::load_iff_file(&save_path)?;
|
let (reloaded_bmp, _) = IndexedBitmap::load_iff_file(&save_path)?;
|
||||||
assert_eq!(320, reloaded_bmp.width());
|
assert_eq!(320, reloaded_bmp.width());
|
||||||
assert_eq!(200, reloaded_bmp.height());
|
assert_eq!(200, reloaded_bmp.height());
|
||||||
assert_eq!(reloaded_bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW_2);
|
assert_eq!(reloaded_bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW_2);
|
||||||
|
|
||||||
// first image, ILBM format
|
// first image, ILBM format
|
||||||
|
|
||||||
let (bmp, palette) = Bitmap::load_iff_file(Path::new("./test-assets/test_ilbm_image.lbm"))?;
|
let (bmp, palette) = IndexedBitmap::load_iff_file(Path::new("./test-assets/test_ilbm_image.lbm"))?;
|
||||||
assert_eq!(320, bmp.width());
|
assert_eq!(320, bmp.width());
|
||||||
assert_eq!(200, bmp.height());
|
assert_eq!(200, bmp.height());
|
||||||
assert_eq!(bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW);
|
assert_eq!(bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW);
|
||||||
|
|
||||||
let save_path = tmp_dir.path().join("test_save_ilbm_image.lbm");
|
let save_path = tmp_dir.path().join("test_save_ilbm_image.lbm");
|
||||||
bmp.to_iff_file(&save_path, &palette, IffFormat::Ilbm)?;
|
bmp.to_iff_file(&save_path, &palette, IffFormat::Ilbm)?;
|
||||||
let (reloaded_bmp, _) = Bitmap::load_iff_file(&save_path)?;
|
let (reloaded_bmp, _) = IndexedBitmap::load_iff_file(&save_path)?;
|
||||||
assert_eq!(320, reloaded_bmp.width());
|
assert_eq!(320, reloaded_bmp.width());
|
||||||
assert_eq!(200, reloaded_bmp.height());
|
assert_eq!(200, reloaded_bmp.height());
|
||||||
assert_eq!(reloaded_bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW);
|
assert_eq!(reloaded_bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW);
|
||||||
|
|
||||||
// second image, ILBM format
|
// second image, ILBM format
|
||||||
|
|
||||||
let (bmp, palette) = Bitmap::load_iff_file(Path::new("./test-assets/test_ilbm_image2.lbm"))?;
|
let (bmp, palette) = IndexedBitmap::load_iff_file(Path::new("./test-assets/test_ilbm_image2.lbm"))?;
|
||||||
assert_eq!(320, bmp.width());
|
assert_eq!(320, bmp.width());
|
||||||
assert_eq!(200, bmp.height());
|
assert_eq!(200, bmp.height());
|
||||||
assert_eq!(bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW_2);
|
assert_eq!(bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW_2);
|
||||||
|
|
||||||
let save_path = tmp_dir.path().join("test_save_ilbm_image2.lbm");
|
let save_path = tmp_dir.path().join("test_save_ilbm_image2.lbm");
|
||||||
bmp.to_iff_file(&save_path, &palette, IffFormat::Ilbm)?;
|
bmp.to_iff_file(&save_path, &palette, IffFormat::Ilbm)?;
|
||||||
let (reloaded_bmp, _) = Bitmap::load_iff_file(&save_path)?;
|
let (reloaded_bmp, _) = IndexedBitmap::load_iff_file(&save_path)?;
|
||||||
assert_eq!(320, reloaded_bmp.width());
|
assert_eq!(320, reloaded_bmp.width());
|
||||||
assert_eq!(200, reloaded_bmp.height());
|
assert_eq!(200, reloaded_bmp.height());
|
||||||
assert_eq!(reloaded_bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW_2);
|
assert_eq!(reloaded_bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW_2);
|
551
ggdt/src/graphics/bitmap/indexed/blit.rs
Normal file
551
ggdt/src/graphics/bitmap/indexed/blit.rs
Normal file
|
@ -0,0 +1,551 @@
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use crate::graphics::bitmap::blit::{clip_blit, per_pixel_blit, per_pixel_flipped_blit, per_pixel_rotozoom_blit};
|
||||||
|
use crate::graphics::bitmap::indexed::IndexedBitmap;
|
||||||
|
use crate::graphics::bitmapatlas::BitmapAtlas;
|
||||||
|
use crate::graphics::blendmap::BlendMap;
|
||||||
|
use crate::math::rect::Rect;
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq)]
|
||||||
|
pub enum IndexedBlitMethod {
|
||||||
|
/// Solid blit, no transparency or other per-pixel adjustments.
|
||||||
|
Solid,
|
||||||
|
/// Same as [IndexedBlitMethod::Solid] but the drawn image can also be flipped horizontally
|
||||||
|
/// and/or vertically.
|
||||||
|
SolidFlipped {
|
||||||
|
horizontal_flip: bool,
|
||||||
|
vertical_flip: bool,
|
||||||
|
},
|
||||||
|
/// Transparent blit, the specified source color pixels are skipped.
|
||||||
|
Transparent(u8),
|
||||||
|
/// Same as [IndexedBlitMethod::Transparent] but the drawn image can also be flipped horizontally
|
||||||
|
/// and/or vertically.
|
||||||
|
TransparentFlipped {
|
||||||
|
transparent_color: u8,
|
||||||
|
horizontal_flip: bool,
|
||||||
|
vertical_flip: bool,
|
||||||
|
},
|
||||||
|
/// Same as [IndexedBlitMethod::Transparent] except that the visible pixels on the destination are all
|
||||||
|
/// drawn using the same color.
|
||||||
|
TransparentSingle {
|
||||||
|
transparent_color: u8,
|
||||||
|
draw_color: u8,
|
||||||
|
},
|
||||||
|
/// Combination of [IndexedBlitMethod::TransparentFlipped] and [IndexedBlitMethod::TransparentSingle].
|
||||||
|
TransparentFlippedSingle {
|
||||||
|
transparent_color: u8,
|
||||||
|
horizontal_flip: bool,
|
||||||
|
vertical_flip: bool,
|
||||||
|
draw_color: u8,
|
||||||
|
},
|
||||||
|
/// Same as [IndexedBlitMethod::Solid] except that the drawn pixels have their color indices offset
|
||||||
|
/// by the amount given.
|
||||||
|
SolidOffset(u8),
|
||||||
|
/// Combination of [IndexedBlitMethod::SolidFlipped] and [IndexedBlitMethod::SolidOffset].
|
||||||
|
SolidFlippedOffset {
|
||||||
|
horizontal_flip: bool,
|
||||||
|
vertical_flip: bool,
|
||||||
|
offset: u8,
|
||||||
|
},
|
||||||
|
/// Same as [IndexedBlitMethod::Transparent] except that the drawn pixels have their color indices
|
||||||
|
/// offset by the amount given. The transparent color check is not affected by the offset and
|
||||||
|
/// is always treated as an absolute palette color index.
|
||||||
|
TransparentOffset { transparent_color: u8, offset: u8 },
|
||||||
|
/// Combination of [IndexedBlitMethod::TransparentFlipped] and [IndexedBlitMethod::TransparentOffset].
|
||||||
|
TransparentFlippedOffset {
|
||||||
|
transparent_color: u8,
|
||||||
|
horizontal_flip: bool,
|
||||||
|
vertical_flip: bool,
|
||||||
|
offset: u8,
|
||||||
|
},
|
||||||
|
/// Rotozoom blit, works the same as [IndexedBlitMethod::Solid] except that rotation and scaling is
|
||||||
|
/// performed.
|
||||||
|
RotoZoom {
|
||||||
|
angle: f32,
|
||||||
|
scale_x: f32,
|
||||||
|
scale_y: f32,
|
||||||
|
},
|
||||||
|
/// Same as [IndexedBlitMethod::RotoZoom] except that the specified source color pixels are skipped.
|
||||||
|
RotoZoomTransparent {
|
||||||
|
angle: f32,
|
||||||
|
scale_x: f32,
|
||||||
|
scale_y: f32,
|
||||||
|
transparent_color: u8,
|
||||||
|
},
|
||||||
|
/// Same as [IndexedBlitMethod::RotoZoom] except that the drawn pixels have their color indices
|
||||||
|
/// offset by the amount given.
|
||||||
|
RotoZoomOffset {
|
||||||
|
angle: f32,
|
||||||
|
scale_x: f32,
|
||||||
|
scale_y: f32,
|
||||||
|
offset: u8,
|
||||||
|
},
|
||||||
|
/// Same as [IndexedBlitMethod::RotoZoomTransparent] except that the drawn pixels have their color
|
||||||
|
/// indices offset by the amount given. The transparent color check is not affected by the
|
||||||
|
/// offset and is always treated as an absolute palette color index.
|
||||||
|
RotoZoomTransparentOffset {
|
||||||
|
angle: f32,
|
||||||
|
scale_x: f32,
|
||||||
|
scale_y: f32,
|
||||||
|
transparent_color: u8,
|
||||||
|
offset: u8,
|
||||||
|
},
|
||||||
|
SolidBlended {
|
||||||
|
blend_map: Rc<BlendMap>,
|
||||||
|
},
|
||||||
|
SolidFlippedBlended {
|
||||||
|
horizontal_flip: bool,
|
||||||
|
vertical_flip: bool,
|
||||||
|
blend_map: Rc<BlendMap>,
|
||||||
|
},
|
||||||
|
TransparentBlended {
|
||||||
|
transparent_color: u8,
|
||||||
|
blend_map: Rc<BlendMap>,
|
||||||
|
},
|
||||||
|
TransparentFlippedBlended {
|
||||||
|
transparent_color: u8,
|
||||||
|
horizontal_flip: bool,
|
||||||
|
vertical_flip: bool,
|
||||||
|
blend_map: Rc<BlendMap>,
|
||||||
|
},
|
||||||
|
RotoZoomBlended {
|
||||||
|
angle: f32,
|
||||||
|
scale_x: f32,
|
||||||
|
scale_y: f32,
|
||||||
|
blend_map: Rc<BlendMap>,
|
||||||
|
},
|
||||||
|
RotoZoomTransparentBlended {
|
||||||
|
angle: f32,
|
||||||
|
scale_x: f32,
|
||||||
|
scale_y: f32,
|
||||||
|
transparent_color: u8,
|
||||||
|
blend_map: Rc<BlendMap>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IndexedBitmap {
|
||||||
|
pub unsafe fn solid_blended_blit(
|
||||||
|
&mut self,
|
||||||
|
src: &Self,
|
||||||
|
src_region: &Rect,
|
||||||
|
dest_x: i32,
|
||||||
|
dest_y: i32,
|
||||||
|
blend_map: Rc<BlendMap>,
|
||||||
|
) {
|
||||||
|
per_pixel_blit(
|
||||||
|
self, src, src_region, dest_x, dest_y,
|
||||||
|
|src_pixels, dest_pixels| {
|
||||||
|
if let Some(blended_pixel) = blend_map.blend(*src_pixels, *dest_pixels) {
|
||||||
|
*dest_pixels = blended_pixel;
|
||||||
|
} else {
|
||||||
|
*dest_pixels = *src_pixels;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn solid_flipped_blended_blit(
|
||||||
|
&mut self,
|
||||||
|
src: &Self,
|
||||||
|
src_region: &Rect,
|
||||||
|
dest_x: i32,
|
||||||
|
dest_y: i32,
|
||||||
|
horizontal_flip: bool,
|
||||||
|
vertical_flip: bool,
|
||||||
|
blend_map: Rc<BlendMap>,
|
||||||
|
) {
|
||||||
|
per_pixel_flipped_blit(
|
||||||
|
self, src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip,
|
||||||
|
|src_pixels, dest_pixels| {
|
||||||
|
if let Some(blended_pixel) = blend_map.blend(*src_pixels, *dest_pixels) {
|
||||||
|
*dest_pixels = blended_pixel;
|
||||||
|
} else {
|
||||||
|
*dest_pixels = *src_pixels;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn solid_palette_offset_blit(
|
||||||
|
&mut self,
|
||||||
|
src: &Self,
|
||||||
|
src_region: &Rect,
|
||||||
|
dest_x: i32,
|
||||||
|
dest_y: i32,
|
||||||
|
offset: u8,
|
||||||
|
) {
|
||||||
|
per_pixel_blit(
|
||||||
|
self, src, src_region, dest_x, dest_y,
|
||||||
|
|src_pixels, dest_pixels| {
|
||||||
|
*dest_pixels = (*src_pixels).wrapping_add(offset);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn solid_flipped_palette_offset_blit(
|
||||||
|
&mut self,
|
||||||
|
src: &Self,
|
||||||
|
src_region: &Rect,
|
||||||
|
dest_x: i32,
|
||||||
|
dest_y: i32,
|
||||||
|
horizontal_flip: bool,
|
||||||
|
vertical_flip: bool,
|
||||||
|
offset: u8,
|
||||||
|
) {
|
||||||
|
per_pixel_flipped_blit(
|
||||||
|
self, src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip,
|
||||||
|
|src_pixels, dest_pixels| {
|
||||||
|
*dest_pixels = (*src_pixels).wrapping_add(offset);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn transparent_blended_blit(
|
||||||
|
&mut self,
|
||||||
|
src: &Self,
|
||||||
|
src_region: &Rect,
|
||||||
|
dest_x: i32,
|
||||||
|
dest_y: i32,
|
||||||
|
transparent_color: u8,
|
||||||
|
blend_map: Rc<BlendMap>,
|
||||||
|
) {
|
||||||
|
per_pixel_blit(
|
||||||
|
self, src, src_region, dest_x, dest_y,
|
||||||
|
|src_pixels, dest_pixels| {
|
||||||
|
if *src_pixels != transparent_color {
|
||||||
|
if let Some(blended_pixel) = blend_map.blend(*src_pixels, *dest_pixels) {
|
||||||
|
*dest_pixels = blended_pixel;
|
||||||
|
} else {
|
||||||
|
*dest_pixels = *src_pixels;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn transparent_flipped_blended_blit(
|
||||||
|
&mut self,
|
||||||
|
src: &Self,
|
||||||
|
src_region: &Rect,
|
||||||
|
dest_x: i32,
|
||||||
|
dest_y: i32,
|
||||||
|
transparent_color: u8,
|
||||||
|
horizontal_flip: bool,
|
||||||
|
vertical_flip: bool,
|
||||||
|
blend_map: Rc<BlendMap>,
|
||||||
|
) {
|
||||||
|
per_pixel_flipped_blit(
|
||||||
|
self, src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip,
|
||||||
|
|src_pixels, dest_pixels| {
|
||||||
|
if *src_pixels != transparent_color {
|
||||||
|
if let Some(blended_pixel) = blend_map.blend(*src_pixels, *dest_pixels) {
|
||||||
|
*dest_pixels = blended_pixel;
|
||||||
|
} else {
|
||||||
|
*dest_pixels = *src_pixels;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn transparent_palette_offset_blit(
|
||||||
|
&mut self,
|
||||||
|
src: &Self,
|
||||||
|
src_region: &Rect,
|
||||||
|
dest_x: i32,
|
||||||
|
dest_y: i32,
|
||||||
|
transparent_color: u8,
|
||||||
|
offset: u8,
|
||||||
|
) {
|
||||||
|
per_pixel_blit(
|
||||||
|
self, src, src_region, dest_x, dest_y,
|
||||||
|
|src_pixels, dest_pixels| {
|
||||||
|
if *src_pixels != transparent_color {
|
||||||
|
*dest_pixels = (*src_pixels).wrapping_add(offset);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn transparent_flipped_palette_offset_blit(
|
||||||
|
&mut self,
|
||||||
|
src: &Self,
|
||||||
|
src_region: &Rect,
|
||||||
|
dest_x: i32,
|
||||||
|
dest_y: i32,
|
||||||
|
transparent_color: u8,
|
||||||
|
horizontal_flip: bool,
|
||||||
|
vertical_flip: bool,
|
||||||
|
offset: u8,
|
||||||
|
) {
|
||||||
|
per_pixel_flipped_blit(
|
||||||
|
self, src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip,
|
||||||
|
|src_pixels, dest_pixels| {
|
||||||
|
if *src_pixels != transparent_color {
|
||||||
|
*dest_pixels = (*src_pixels).wrapping_add(offset);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn rotozoom_blended_blit(
|
||||||
|
&mut self,
|
||||||
|
src: &Self,
|
||||||
|
src_region: &Rect,
|
||||||
|
dest_x: i32,
|
||||||
|
dest_y: i32,
|
||||||
|
angle: f32,
|
||||||
|
scale_x: f32,
|
||||||
|
scale_y: f32,
|
||||||
|
blend_map: Rc<BlendMap>,
|
||||||
|
) {
|
||||||
|
per_pixel_rotozoom_blit(
|
||||||
|
self, src, src_region, dest_x, dest_y, angle, scale_x, scale_y,
|
||||||
|
|src_pixel, dest_bitmap, draw_x, draw_y| {
|
||||||
|
if let Some(dest_pixel) = dest_bitmap.get_pixel(draw_x, draw_y) {
|
||||||
|
let draw_pixel = if let Some(blended_pixel) = blend_map.blend(src_pixel, dest_pixel) {
|
||||||
|
blended_pixel
|
||||||
|
} else {
|
||||||
|
src_pixel
|
||||||
|
};
|
||||||
|
dest_bitmap.set_pixel(draw_x, draw_y, draw_pixel);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn rotozoom_transparent_blended_blit(
|
||||||
|
&mut self,
|
||||||
|
src: &Self,
|
||||||
|
src_region: &Rect,
|
||||||
|
dest_x: i32,
|
||||||
|
dest_y: i32,
|
||||||
|
angle: f32,
|
||||||
|
scale_x: f32,
|
||||||
|
scale_y: f32,
|
||||||
|
transparent_color: u8,
|
||||||
|
blend_map: Rc<BlendMap>,
|
||||||
|
) {
|
||||||
|
per_pixel_rotozoom_blit(
|
||||||
|
self, src, src_region, dest_x, dest_y, angle, scale_x, scale_y,
|
||||||
|
|src_pixel, dest_bitmap, draw_x, draw_y| {
|
||||||
|
if transparent_color != src_pixel {
|
||||||
|
if let Some(dest_pixel) = dest_bitmap.get_pixel(draw_x, draw_y) {
|
||||||
|
let draw_pixel = if let Some(blended_pixel) = blend_map.blend(src_pixel, dest_pixel) {
|
||||||
|
blended_pixel
|
||||||
|
} else {
|
||||||
|
src_pixel
|
||||||
|
};
|
||||||
|
dest_bitmap.set_pixel(draw_x, draw_y, draw_pixel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn rotozoom_palette_offset_blit(
|
||||||
|
&mut self,
|
||||||
|
src: &Self,
|
||||||
|
src_region: &Rect,
|
||||||
|
dest_x: i32,
|
||||||
|
dest_y: i32,
|
||||||
|
angle: f32,
|
||||||
|
scale_x: f32,
|
||||||
|
scale_y: f32,
|
||||||
|
offset: u8,
|
||||||
|
) {
|
||||||
|
per_pixel_rotozoom_blit(
|
||||||
|
self, src, src_region, dest_x, dest_y, angle, scale_x, scale_y,
|
||||||
|
|src_pixel, dest_bitmap, draw_x, draw_y| {
|
||||||
|
let src_pixel = src_pixel.wrapping_add(offset);
|
||||||
|
dest_bitmap.set_pixel(draw_x, draw_y, src_pixel);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn rotozoom_transparent_palette_offset_blit(
|
||||||
|
&mut self,
|
||||||
|
src: &Self,
|
||||||
|
src_region: &Rect,
|
||||||
|
dest_x: i32,
|
||||||
|
dest_y: i32,
|
||||||
|
angle: f32,
|
||||||
|
scale_x: f32,
|
||||||
|
scale_y: f32,
|
||||||
|
transparent_color: u8,
|
||||||
|
offset: u8,
|
||||||
|
) {
|
||||||
|
per_pixel_rotozoom_blit(
|
||||||
|
self, src, src_region, dest_x, dest_y, angle, scale_x, scale_y,
|
||||||
|
|src_pixel, dest_bitmap, draw_x, draw_y| {
|
||||||
|
if transparent_color != src_pixel {
|
||||||
|
let src_pixel = src_pixel.wrapping_add(offset);
|
||||||
|
dest_bitmap.set_pixel(draw_x, draw_y, src_pixel);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn blit_region(
|
||||||
|
&mut self,
|
||||||
|
method: IndexedBlitMethod,
|
||||||
|
src: &Self,
|
||||||
|
src_region: &Rect,
|
||||||
|
mut dest_x: i32,
|
||||||
|
mut dest_y: i32,
|
||||||
|
) {
|
||||||
|
// make sure the source region is clipped or even valid at all for the source bitmap given
|
||||||
|
let mut src_region = *src_region;
|
||||||
|
if !src_region.clamp_to(&src.clip_region) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// some blit methods need to handle clipping a bit differently than others
|
||||||
|
use IndexedBlitMethod::*;
|
||||||
|
match method {
|
||||||
|
// rotozoom blits internally clip per-pixel right now ... and regardless, the normal
|
||||||
|
// clip_blit() function wouldn't handle a rotozoom blit destination region anyway ...
|
||||||
|
RotoZoom { .. } => {}
|
||||||
|
RotoZoomBlended { .. } => {}
|
||||||
|
RotoZoomOffset { .. } => {}
|
||||||
|
RotoZoomTransparent { .. } => {}
|
||||||
|
RotoZoomTransparentBlended { .. } => {}
|
||||||
|
RotoZoomTransparentOffset { .. } => {}
|
||||||
|
|
||||||
|
// set axis flip arguments
|
||||||
|
SolidFlipped { horizontal_flip, vertical_flip, .. } |
|
||||||
|
SolidFlippedBlended { horizontal_flip, vertical_flip, .. } |
|
||||||
|
SolidFlippedOffset { horizontal_flip, vertical_flip, .. } |
|
||||||
|
TransparentFlipped { horizontal_flip, vertical_flip, .. } |
|
||||||
|
TransparentFlippedBlended { horizontal_flip, vertical_flip, .. } |
|
||||||
|
TransparentFlippedSingle { horizontal_flip, vertical_flip, .. } |
|
||||||
|
TransparentFlippedOffset { horizontal_flip, vertical_flip, .. } => {
|
||||||
|
if !clip_blit(
|
||||||
|
self.clip_region(),
|
||||||
|
&mut src_region,
|
||||||
|
&mut dest_x,
|
||||||
|
&mut dest_y,
|
||||||
|
horizontal_flip,
|
||||||
|
vertical_flip,
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise clip like normal!
|
||||||
|
_ => {
|
||||||
|
if !clip_blit(
|
||||||
|
self.clip_region(),
|
||||||
|
&mut src_region,
|
||||||
|
&mut dest_x,
|
||||||
|
&mut dest_y,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
self.blit_region_unchecked(method, src, &src_region, dest_x, dest_y);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[rustfmt::skip]
|
||||||
|
pub unsafe fn blit_region_unchecked(
|
||||||
|
&mut self,
|
||||||
|
method: IndexedBlitMethod,
|
||||||
|
src: &Self,
|
||||||
|
src_region: &Rect,
|
||||||
|
dest_x: i32,
|
||||||
|
dest_y: i32,
|
||||||
|
) {
|
||||||
|
use IndexedBlitMethod::*;
|
||||||
|
match method {
|
||||||
|
Solid => self.solid_blit(src, src_region, dest_x, dest_y),
|
||||||
|
SolidFlipped { horizontal_flip, vertical_flip } => {
|
||||||
|
self.solid_flipped_blit(src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip)
|
||||||
|
}
|
||||||
|
SolidOffset(offset) => self.solid_palette_offset_blit(src, src_region, dest_x, dest_y, offset),
|
||||||
|
SolidFlippedOffset { horizontal_flip, vertical_flip, offset } => {
|
||||||
|
self.solid_flipped_palette_offset_blit(src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip, offset)
|
||||||
|
}
|
||||||
|
Transparent(transparent_color) => {
|
||||||
|
self.transparent_blit(src, src_region, dest_x, dest_y, transparent_color)
|
||||||
|
}
|
||||||
|
TransparentFlipped { transparent_color, horizontal_flip, vertical_flip } => {
|
||||||
|
self.transparent_flipped_blit(src, src_region, dest_x, dest_y, transparent_color, horizontal_flip, vertical_flip)
|
||||||
|
}
|
||||||
|
TransparentOffset { transparent_color, offset } => {
|
||||||
|
self.transparent_palette_offset_blit(src, src_region, dest_x, dest_y, transparent_color, offset)
|
||||||
|
}
|
||||||
|
TransparentFlippedOffset { transparent_color, horizontal_flip, vertical_flip, offset } => {
|
||||||
|
self.transparent_flipped_palette_offset_blit(src, src_region, dest_x, dest_y, transparent_color, horizontal_flip, vertical_flip, offset)
|
||||||
|
}
|
||||||
|
TransparentSingle { transparent_color, draw_color } => {
|
||||||
|
self.transparent_single_color_blit(src, src_region, dest_x, dest_y, transparent_color, draw_color)
|
||||||
|
}
|
||||||
|
TransparentFlippedSingle { transparent_color, horizontal_flip, vertical_flip, draw_color } => {
|
||||||
|
self.transparent_flipped_single_color_blit(src, src_region, dest_x, dest_y, transparent_color, horizontal_flip, vertical_flip, draw_color)
|
||||||
|
}
|
||||||
|
RotoZoom { angle, scale_x, scale_y } => {
|
||||||
|
self.rotozoom_blit(src, src_region, dest_x, dest_y, angle, scale_x, scale_y)
|
||||||
|
}
|
||||||
|
RotoZoomOffset { angle, scale_x, scale_y, offset } => {
|
||||||
|
self.rotozoom_palette_offset_blit(src, src_region, dest_x, dest_y, angle, scale_x, scale_y, offset)
|
||||||
|
}
|
||||||
|
RotoZoomTransparent { angle, scale_x, scale_y, transparent_color } => {
|
||||||
|
self.rotozoom_transparent_blit(src, src_region, dest_x, dest_y, angle, scale_x, scale_y, transparent_color)
|
||||||
|
}
|
||||||
|
RotoZoomTransparentOffset { angle, scale_x, scale_y, transparent_color, offset } => {
|
||||||
|
self.rotozoom_transparent_palette_offset_blit(src, src_region, dest_x, dest_y, angle, scale_x, scale_y, transparent_color, offset)
|
||||||
|
}
|
||||||
|
SolidBlended { blend_map } => {
|
||||||
|
self.solid_blended_blit(src, src_region, dest_x, dest_y, blend_map)
|
||||||
|
}
|
||||||
|
SolidFlippedBlended { horizontal_flip, vertical_flip, blend_map } => {
|
||||||
|
self.solid_flipped_blended_blit(src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip, blend_map)
|
||||||
|
}
|
||||||
|
TransparentBlended { transparent_color, blend_map } => {
|
||||||
|
self.transparent_blended_blit(src, src_region, dest_x, dest_y, transparent_color, blend_map)
|
||||||
|
}
|
||||||
|
TransparentFlippedBlended { transparent_color, horizontal_flip, vertical_flip, blend_map } => {
|
||||||
|
self.transparent_flipped_blended_blit(src, src_region, dest_x, dest_y, transparent_color, horizontal_flip, vertical_flip, blend_map)
|
||||||
|
}
|
||||||
|
RotoZoomBlended { angle, scale_x, scale_y, blend_map } => {
|
||||||
|
self.rotozoom_blended_blit(src, src_region, dest_x, dest_y, angle, scale_x, scale_y, blend_map)
|
||||||
|
}
|
||||||
|
RotoZoomTransparentBlended { angle, scale_x, scale_y, transparent_color, blend_map } => {
|
||||||
|
self.rotozoom_transparent_blended_blit(src, src_region, dest_x, dest_y, angle, scale_x, scale_y, transparent_color, blend_map)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn blit(&mut self, method: IndexedBlitMethod, src: &Self, x: i32, y: i32) {
|
||||||
|
let src_region = Rect::new(0, 0, src.width, src.height);
|
||||||
|
self.blit_region(method, src, &src_region, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn blit_atlas(&mut self, method: IndexedBlitMethod, src: &BitmapAtlas<Self>, index: usize, x: i32, y: i32) {
|
||||||
|
if let Some(src_region) = src.get(index) {
|
||||||
|
self.blit_region(method, src.bitmap(), src_region, x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn blit_unchecked(&mut self, method: 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn blit_atlas_unchecked(&mut self, method: IndexedBlitMethod, src: &BitmapAtlas<Self>, index: usize, x: i32, y: i32) {
|
||||||
|
if let Some(src_region) = src.get(index) {
|
||||||
|
self.blit_region_unchecked(method, src.bitmap(), &src_region, x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
46
ggdt/src/graphics/bitmap/indexed/mod.rs
Normal file
46
ggdt/src/graphics/bitmap/indexed/mod.rs
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use crate::graphics::bitmap::{Bitmap, BitmapError};
|
||||||
|
use crate::graphics::palette::Palette;
|
||||||
|
|
||||||
|
pub mod blit;
|
||||||
|
pub mod primitives;
|
||||||
|
|
||||||
|
pub type IndexedBitmap = Bitmap<u8>;
|
||||||
|
|
||||||
|
impl IndexedBitmap {
|
||||||
|
|
||||||
|
pub fn load_file(path: &Path) -> Result<(Self, Palette), BitmapError> {
|
||||||
|
if let Some(extension) = path.extension() {
|
||||||
|
let extension = extension.to_ascii_lowercase();
|
||||||
|
match extension.to_str() {
|
||||||
|
Some("pcx") => Ok(Self::load_pcx_file(path)?),
|
||||||
|
Some("gif") => Ok(Self::load_gif_file(path)?),
|
||||||
|
Some("iff") | Some("lbm") | Some("pbm") | Some("bbm") => {
|
||||||
|
Ok(Self::load_iff_file(path)?)
|
||||||
|
}
|
||||||
|
_ => Err(BitmapError::UnknownFileType(String::from(
|
||||||
|
"Unrecognized file extension",
|
||||||
|
))),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(BitmapError::UnknownFileType(String::from(
|
||||||
|
"No file extension",
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Copies and converts the entire pixel data from this bitmap to a destination expecting
|
||||||
|
/// 32-bit ARGB-format pixel data. This can be used to display the contents of the bitmap
|
||||||
|
/// on-screen by using an SDL Surface, OpenGL texture, etc as the destination.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `dest`: destination 32-bit ARGB pixel buffer to copy converted pixels to
|
||||||
|
/// * `palette`: the 256 colour palette to use during pixel conversion
|
||||||
|
pub fn copy_as_argb_to(&self, dest: &mut [u32], palette: &Palette) {
|
||||||
|
for (src, dest) in self.pixels().iter().zip(dest.iter_mut()) {
|
||||||
|
*dest = palette[*src];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
243
ggdt/src/graphics/bitmap/indexed/primitives.rs
Normal file
243
ggdt/src/graphics/bitmap/indexed/primitives.rs
Normal file
|
@ -0,0 +1,243 @@
|
||||||
|
use std::mem::swap;
|
||||||
|
|
||||||
|
use crate::graphics::bitmap::indexed::IndexedBitmap;
|
||||||
|
use crate::graphics::blendmap::BlendMap;
|
||||||
|
use crate::math::rect::Rect;
|
||||||
|
|
||||||
|
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. If the
|
||||||
|
/// coordinates lie outside of the bitmaps clipping region, no pixels will be changed.
|
||||||
|
#[inline]
|
||||||
|
pub fn set_blended_pixel(&mut self, x: i32, y: i32, color: u8, blend_map: &BlendMap) {
|
||||||
|
if let Some(pixels) = self.pixels_at_mut(x, y) {
|
||||||
|
let dest_color = pixels[0];
|
||||||
|
if let Some(blended_color) = blend_map.blend(color, dest_color) {
|
||||||
|
pixels[0] = blended_color;
|
||||||
|
} else {
|
||||||
|
pixels[0] = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the pixel at the given coordinates using a blended color via the specified blend map,
|
||||||
|
/// or using the color specified if the blend map does not include the given color. The
|
||||||
|
/// coordinates are not checked for validity, so it is up to you to ensure they lie within the
|
||||||
|
/// bounds of the bitmap.
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn set_blended_pixel_unchecked(&mut self, x: i32, y: i32, color: u8, blend_map: &BlendMap) {
|
||||||
|
let p = self.pixels_at_mut_ptr_unchecked(x, y);
|
||||||
|
if let Some(blended_color) = blend_map.blend(color, *p) {
|
||||||
|
*p = blended_color;
|
||||||
|
} else {
|
||||||
|
*p = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draws a line from x1,y1 to x2,y2 by blending the drawn pixels using the given blend map,
|
||||||
|
/// or the color specified if the blend map does not include this color.
|
||||||
|
pub fn blended_line(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: u8, blend_map: &BlendMap) {
|
||||||
|
if let Some(blend_mapping) = blend_map.get_mapping(color) {
|
||||||
|
let mut dx = x1;
|
||||||
|
let mut dy = y1;
|
||||||
|
let delta_x = x2 - x1;
|
||||||
|
let delta_y = y2 - y1;
|
||||||
|
let delta_x_abs = delta_x.abs();
|
||||||
|
let delta_y_abs = delta_y.abs();
|
||||||
|
let delta_x_sign = delta_x.signum();
|
||||||
|
let delta_y_sign = delta_y.signum();
|
||||||
|
let mut x = delta_x_abs / 2;
|
||||||
|
let mut y = delta_y_abs / 2;
|
||||||
|
let offset_x_inc = delta_x_sign;
|
||||||
|
let offset_y_inc = delta_y_sign * self.width as i32;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
// safety: while we are blindly getting a pointer to this x/y coordinate, we don't
|
||||||
|
// write to it unless we know the coordinates are in bounds.
|
||||||
|
// TODO: should be ok ... ? or am i making too many assumptions about memory layout?
|
||||||
|
let mut dest = self.pixels_at_mut_ptr_unchecked(x1, y1);
|
||||||
|
|
||||||
|
if self.is_xy_visible(dx, dy) {
|
||||||
|
*dest = blend_mapping[*dest as usize];
|
||||||
|
}
|
||||||
|
|
||||||
|
if delta_x_abs >= delta_y_abs {
|
||||||
|
for _ in 0..delta_x_abs {
|
||||||
|
y += delta_y_abs;
|
||||||
|
|
||||||
|
if y >= delta_x_abs {
|
||||||
|
y -= delta_x_abs;
|
||||||
|
dy += delta_y_sign;
|
||||||
|
dest = dest.offset(offset_y_inc as isize);
|
||||||
|
}
|
||||||
|
|
||||||
|
dx += delta_x_sign;
|
||||||
|
dest = dest.offset(offset_x_inc as isize);
|
||||||
|
|
||||||
|
if self.is_xy_visible(dx, dy) {
|
||||||
|
*dest = blend_mapping[*dest as usize];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for _ in 0..delta_y_abs {
|
||||||
|
x += delta_x_abs;
|
||||||
|
|
||||||
|
if x >= delta_y_abs {
|
||||||
|
x -= delta_y_abs;
|
||||||
|
dx += delta_x_sign;
|
||||||
|
dest = dest.offset(offset_x_inc as isize);
|
||||||
|
}
|
||||||
|
|
||||||
|
dy += delta_y_sign;
|
||||||
|
dest = dest.offset(offset_y_inc as isize);
|
||||||
|
|
||||||
|
if self.is_xy_visible(dx, dy) {
|
||||||
|
*dest = blend_mapping[*dest as usize];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.line(x1, y1, x2, y2, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draws a horizontal line from x1,y to x2,y by blending the drawn pixels using the given
|
||||||
|
/// blend map, or the color specified if the blend map does not include this color.
|
||||||
|
pub fn blended_horiz_line(&mut self, x1: i32, x2: i32, y: i32, color: u8, blend_map: &BlendMap) {
|
||||||
|
if let Some(blend_mapping) = blend_map.get_mapping(color) {
|
||||||
|
let mut region = Rect::from_coords(x1, y, x2, y);
|
||||||
|
if region.clamp_to(&self.clip_region) {
|
||||||
|
unsafe {
|
||||||
|
let dest = self.pixels_at_mut_unchecked(region.x, region.y);
|
||||||
|
for x in 0..region.width as usize {
|
||||||
|
dest[x] = blend_mapping[dest[x] as usize];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.horiz_line(x1, x2, y, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draws a vertical line from x,y1 to x,y2 by blending the drawn pixels using the given blend
|
||||||
|
/// map, or the color specified if the blend map does not include this color.
|
||||||
|
pub fn blended_vert_line(&mut self, x: i32, y1: i32, y2: i32, color: u8, blend_map: &BlendMap) {
|
||||||
|
if let Some(blend_mapping) = blend_map.get_mapping(color) {
|
||||||
|
let mut region = Rect::from_coords(x, y1, x, y2);
|
||||||
|
if region.clamp_to(&self.clip_region) {
|
||||||
|
unsafe {
|
||||||
|
let mut dest = self.pixels_at_mut_ptr_unchecked(region.x, region.y);
|
||||||
|
for _ in 0..region.height {
|
||||||
|
*dest = blend_mapping[*dest as usize];
|
||||||
|
dest = dest.add(self.width as usize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.vert_line(x, y1, y2, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draws an empty box (rectangle) using the points x1,y1 and x2,y2 to form the box to be
|
||||||
|
/// drawn, assuming they are specifying the top-left and bottom-right corners respectively.
|
||||||
|
/// The box is drawn by blending the drawn pixels using the given blend map, or the color
|
||||||
|
/// specified if the blend map does not include this color.
|
||||||
|
pub fn blended_rect(&mut self, mut x1: i32, mut y1: i32, mut x2: i32, mut y2: i32, color: u8, blend_map: &BlendMap) {
|
||||||
|
if let Some(blend_mapping) = blend_map.get_mapping(color) {
|
||||||
|
// note: need to manually do all this instead of just relying on Rect::from_coords (which
|
||||||
|
// could otherwise figure all this out for us) mainly just because we need the post-swap
|
||||||
|
// x1,y1,x2,y2 values for post-region-clamping comparison purposes ...
|
||||||
|
if x2 < x1 {
|
||||||
|
swap(&mut x1, &mut x2);
|
||||||
|
}
|
||||||
|
if y2 < y1 {
|
||||||
|
swap(&mut y1, &mut y2);
|
||||||
|
}
|
||||||
|
let mut region = Rect {
|
||||||
|
x: x1,
|
||||||
|
y: y1,
|
||||||
|
width: (x2 - x1 + 1) as u32,
|
||||||
|
height: (y2 - y1 + 1) as u32,
|
||||||
|
};
|
||||||
|
if !region.clamp_to(&self.clip_region) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// note that since we're performing a blend based on the existing destination pixel,
|
||||||
|
// we need to make sure that we don't draw any overlapping corner pixels (where we
|
||||||
|
// would end up blending with the edge of a previously drawn line).
|
||||||
|
// to solve this issue, we just cut off the left-most and right-most pixels for the
|
||||||
|
// two horizontal lines drawn. those corner pixels will be drawn during the vertical
|
||||||
|
// line drawing instead.
|
||||||
|
|
||||||
|
// top line, only if y1 was originally within bounds
|
||||||
|
if y1 == region.y {
|
||||||
|
unsafe {
|
||||||
|
let dest = self.pixels_at_mut_unchecked(region.x, region.y);
|
||||||
|
for x in 1..(region.width - 1) as usize {
|
||||||
|
dest[x] = blend_mapping[dest[x] as usize];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// bottom line, only if y2 was originally within bounds
|
||||||
|
if y2 == region.bottom() {
|
||||||
|
unsafe {
|
||||||
|
let dest = self.pixels_at_mut_unchecked(region.x, region.bottom());
|
||||||
|
for x in 1..(region.width - 1) as usize {
|
||||||
|
dest[x] = blend_mapping[dest[x] as usize];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// left line, only if x1 was originally within bounds
|
||||||
|
if x1 == region.x {
|
||||||
|
unsafe {
|
||||||
|
let mut dest = self.pixels_at_mut_ptr_unchecked(region.x, region.y);
|
||||||
|
for _ in 0..region.height {
|
||||||
|
*dest = blend_mapping[*dest as usize];
|
||||||
|
dest = dest.add(self.width as usize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// right line, only if x2 was originally within bounds
|
||||||
|
if x2 == region.right() {
|
||||||
|
unsafe {
|
||||||
|
let mut dest = self.pixels_at_mut_ptr_unchecked(region.right(), region.y);
|
||||||
|
for _ in 0..region.height {
|
||||||
|
*dest = blend_mapping[*dest as usize];
|
||||||
|
dest = dest.add(self.width as usize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.rect(x1, y1, x2, y2, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draws a filled box (rectangle) using the points x1,y1 and x2,y2 to form the box to be
|
||||||
|
/// drawn, assuming they are specifying the top-left and bottom-right corners respectively. The
|
||||||
|
/// filled box is draw by blending the drawn pixels using the given blend map, or the color
|
||||||
|
/// specified if the blend map does not include this color.
|
||||||
|
pub fn blended_filled_rect(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: u8, blend_map: &BlendMap) {
|
||||||
|
if let Some(blend_mapping) = blend_map.get_mapping(color) {
|
||||||
|
let mut region = Rect::from_coords(x1, y1, x2, y2);
|
||||||
|
if region.clamp_to(&self.clip_region) {
|
||||||
|
unsafe {
|
||||||
|
let mut dest = self.pixels_at_mut_ptr_unchecked(region.x, region.y);
|
||||||
|
for _ in 0..region.height {
|
||||||
|
for x in 0..region.width as usize {
|
||||||
|
let dest_x = dest.offset(x as isize);
|
||||||
|
*dest_x = blend_mapping[*dest_x as usize];
|
||||||
|
}
|
||||||
|
dest = dest.add(self.width as usize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.filled_rect(x1, y1, x2, y2, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,16 +1,16 @@
|
||||||
use std::path::Path;
|
|
||||||
use std::slice;
|
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::graphics::indexed::palette::Palette;
|
use crate::graphics::Pixel;
|
||||||
use crate::math::rect::Rect;
|
use crate::math::rect::Rect;
|
||||||
|
|
||||||
pub mod blit;
|
pub mod blit;
|
||||||
|
pub mod general;
|
||||||
pub mod gif;
|
pub mod gif;
|
||||||
pub mod iff;
|
pub mod iff;
|
||||||
|
pub mod indexed;
|
||||||
pub mod pcx;
|
pub mod pcx;
|
||||||
pub mod primitives;
|
pub mod primitives;
|
||||||
|
pub mod rgb;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum BitmapError {
|
pub enum BitmapError {
|
||||||
|
@ -40,14 +40,14 @@ pub enum BitmapError {
|
||||||
/// here are done with respect to the bitmaps clipping region, where rendering outside of the
|
/// here are done with respect to the bitmaps clipping region, where rendering outside of the
|
||||||
/// clipping region is simply not performed / stops at the clipping boundary.
|
/// clipping region is simply not performed / stops at the clipping boundary.
|
||||||
#[derive(Clone, Eq, PartialEq)]
|
#[derive(Clone, Eq, PartialEq)]
|
||||||
pub struct Bitmap {
|
pub struct Bitmap<PixelType: Pixel> {
|
||||||
width: u32,
|
width: u32,
|
||||||
height: u32,
|
height: u32,
|
||||||
pixels: Box<[u8]>,
|
pixels: Box<[PixelType]>,
|
||||||
clip_region: Rect,
|
clip_region: Rect,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for Bitmap {
|
impl<PixelType: Pixel> std::fmt::Debug for Bitmap<PixelType> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
f.debug_struct("Bitmap")
|
f.debug_struct("Bitmap")
|
||||||
.field("width", &self.width)
|
.field("width", &self.width)
|
||||||
|
@ -57,7 +57,9 @@ impl std::fmt::Debug for Bitmap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Bitmap {
|
impl<PixelType: Pixel> Bitmap<PixelType> {
|
||||||
|
pub const PIXEL_SIZE: usize = std::mem::size_of::<PixelType>();
|
||||||
|
|
||||||
/// Creates a new Bitmap with the specified dimensions.
|
/// Creates a new Bitmap with the specified dimensions.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
|
@ -66,7 +68,7 @@ impl Bitmap {
|
||||||
/// * `height`: the height of the bitmap in pixels
|
/// * `height`: the height of the bitmap in pixels
|
||||||
///
|
///
|
||||||
/// returns: `Result<Bitmap, BitmapError>`
|
/// returns: `Result<Bitmap, BitmapError>`
|
||||||
pub fn new(width: u32, height: u32) -> Result<Bitmap, BitmapError> {
|
pub fn new(width: u32, height: u32) -> Result<Self, BitmapError> {
|
||||||
if width == 0 || height == 0 {
|
if width == 0 || height == 0 {
|
||||||
return Err(BitmapError::InvalidDimensions);
|
return Err(BitmapError::InvalidDimensions);
|
||||||
}
|
}
|
||||||
|
@ -74,7 +76,7 @@ impl Bitmap {
|
||||||
Ok(Bitmap {
|
Ok(Bitmap {
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
pixels: vec![0u8; (width * height) as usize].into_boxed_slice(),
|
pixels: vec![Default::default(); (width * height) as usize].into_boxed_slice(),
|
||||||
clip_region: Rect {
|
clip_region: Rect {
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
|
@ -93,36 +95,16 @@ impl Bitmap {
|
||||||
/// * `region`: the region on the source bitmap to copy from
|
/// * `region`: the region on the source bitmap to copy from
|
||||||
///
|
///
|
||||||
/// returns: `Result<Bitmap, BitmapError>`
|
/// returns: `Result<Bitmap, BitmapError>`
|
||||||
pub fn from(source: &Bitmap, region: &Rect) -> Result<Bitmap, BitmapError> {
|
pub fn from(source: &Self, region: &Rect) -> Result<Self, BitmapError> {
|
||||||
if !source.full_bounds().contains_rect(region) {
|
if !source.full_bounds().contains_rect(region) {
|
||||||
return Err(BitmapError::OutOfBounds);
|
return Err(BitmapError::OutOfBounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut bmp = Bitmap::new(region.width, region.height)?;
|
let mut bmp = Self::new(region.width, region.height)?;
|
||||||
unsafe { bmp.solid_blit(source, region, 0, 0) };
|
unsafe { bmp.solid_blit(source, region, 0, 0) };
|
||||||
Ok(bmp)
|
Ok(bmp)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_file(path: &Path) -> Result<(Bitmap, Palette), BitmapError> {
|
|
||||||
if let Some(extension) = path.extension() {
|
|
||||||
let extension = extension.to_ascii_lowercase();
|
|
||||||
match extension.to_str() {
|
|
||||||
Some("pcx") => Ok(Self::load_pcx_file(path)?),
|
|
||||||
Some("gif") => Ok(Self::load_gif_file(path)?),
|
|
||||||
Some("iff") | Some("lbm") | Some("pbm") | Some("bbm") => {
|
|
||||||
Ok(Self::load_iff_file(path)?)
|
|
||||||
}
|
|
||||||
_ => Err(BitmapError::UnknownFileType(String::from(
|
|
||||||
"Unrecognized file extension",
|
|
||||||
))),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(BitmapError::UnknownFileType(String::from(
|
|
||||||
"No file extension",
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the width of the bitmap in pixels.
|
/// Returns the width of the bitmap in pixels.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn width(&self) -> u32 {
|
pub fn width(&self) -> u32 {
|
||||||
|
@ -183,13 +165,13 @@ impl Bitmap {
|
||||||
|
|
||||||
/// Returns a reference to the raw pixels in this bitmap.
|
/// Returns a reference to the raw pixels in this bitmap.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn pixels(&self) -> &[u8] {
|
pub fn pixels(&self) -> &[PixelType] {
|
||||||
&self.pixels
|
&self.pixels
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a mutable reference to the raw pixels in this bitmap.
|
/// Returns a mutable reference to the raw pixels in this bitmap.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn pixels_mut(&mut self) -> &mut [u8] {
|
pub fn pixels_mut(&mut self) -> &mut [PixelType] {
|
||||||
&mut self.pixels
|
&mut self.pixels
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,7 +179,7 @@ impl Bitmap {
|
||||||
/// given coordinates and extending to the end of the bitmap. If the coordinates given are
|
/// given coordinates and extending to the end of the bitmap. If the coordinates given are
|
||||||
/// outside the bitmap's current clipping region, None is returned.
|
/// outside the bitmap's current clipping region, None is returned.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn pixels_at(&self, x: i32, y: i32) -> Option<&[u8]> {
|
pub fn pixels_at(&self, x: i32, y: i32) -> Option<&[PixelType]> {
|
||||||
if self.is_xy_visible(x, y) {
|
if self.is_xy_visible(x, y) {
|
||||||
let offset = self.get_offset_to_xy(x, y);
|
let offset = self.get_offset_to_xy(x, y);
|
||||||
Some(&self.pixels[offset..])
|
Some(&self.pixels[offset..])
|
||||||
|
@ -210,7 +192,7 @@ impl Bitmap {
|
||||||
/// given coordinates and extending to the end of the bitmap. If the coordinates given are
|
/// given coordinates and extending to the end of the bitmap. If the coordinates given are
|
||||||
/// outside the bitmap's current clipping region, None is returned.
|
/// outside the bitmap's current clipping region, None is returned.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn pixels_at_mut(&mut self, x: i32, y: i32) -> Option<&mut [u8]> {
|
pub fn pixels_at_mut(&mut self, x: i32, y: i32) -> Option<&mut [PixelType]> {
|
||||||
if self.is_xy_visible(x, y) {
|
if self.is_xy_visible(x, y) {
|
||||||
let offset = self.get_offset_to_xy(x, y);
|
let offset = self.get_offset_to_xy(x, y);
|
||||||
Some(&mut self.pixels[offset..])
|
Some(&mut self.pixels[offset..])
|
||||||
|
@ -223,18 +205,18 @@ impl Bitmap {
|
||||||
/// given coordinates and extending to the end of the bitmap. The coordinates are not checked
|
/// 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.
|
/// for validity, so it is up to you to ensure they lie within the bounds of the bitmap.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn pixels_at_unchecked(&self, x: i32, y: i32) -> &[u8] {
|
pub unsafe fn pixels_at_unchecked(&self, x: i32, y: i32) -> &[PixelType] {
|
||||||
let offset = self.get_offset_to_xy(x, y);
|
let offset = self.get_offset_to_xy(x, y);
|
||||||
slice::from_raw_parts(self.pixels.as_ptr().add(offset), self.pixels.len() - offset)
|
std::slice::from_raw_parts(self.pixels.as_ptr().add(offset), self.pixels.len() - offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a mutable unsafe reference to the subset of the raw pixels in this bitmap beginning
|
/// 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
|
/// 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.
|
/// checked for validity, so it is up to you to ensure they lie within the bounds of the bitmap.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn pixels_at_mut_unchecked(&mut self, x: i32, y: i32) -> &mut [u8] {
|
pub unsafe fn pixels_at_mut_unchecked(&mut self, x: i32, y: i32) -> &mut [PixelType] {
|
||||||
let offset = self.get_offset_to_xy(x, y);
|
let offset = self.get_offset_to_xy(x, y);
|
||||||
slice::from_raw_parts_mut(
|
std::slice::from_raw_parts_mut(
|
||||||
self.pixels.as_mut_ptr().add(offset),
|
self.pixels.as_mut_ptr().add(offset),
|
||||||
self.pixels.len() - offset,
|
self.pixels.len() - offset,
|
||||||
)
|
)
|
||||||
|
@ -244,7 +226,7 @@ impl Bitmap {
|
||||||
/// coordinates. If the coordinates given are outside the bitmap's current clipping region,
|
/// coordinates. If the coordinates given are outside the bitmap's current clipping region,
|
||||||
/// None is returned.
|
/// None is returned.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn pixels_at_ptr(&self, x: i32, y: i32) -> Option<*const u8> {
|
pub unsafe fn pixels_at_ptr(&self, x: i32, y: i32) -> Option<*const PixelType> {
|
||||||
if self.is_xy_visible(x, y) {
|
if self.is_xy_visible(x, y) {
|
||||||
let offset = self.get_offset_to_xy(x, y);
|
let offset = self.get_offset_to_xy(x, y);
|
||||||
Some(self.pixels.as_ptr().add(offset))
|
Some(self.pixels.as_ptr().add(offset))
|
||||||
|
@ -257,7 +239,7 @@ impl Bitmap {
|
||||||
/// given coordinates. If the coordinates given are outside the bitmap's current clipping
|
/// given coordinates. If the coordinates given are outside the bitmap's current clipping
|
||||||
/// region, None is returned.
|
/// region, None is returned.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn pixels_at_mut_ptr(&mut self, x: i32, y: i32) -> Option<*mut u8> {
|
pub unsafe fn pixels_at_mut_ptr(&mut self, x: i32, y: i32) -> Option<*mut PixelType> {
|
||||||
if self.is_xy_visible(x, y) {
|
if self.is_xy_visible(x, y) {
|
||||||
let offset = self.get_offset_to_xy(x, y);
|
let offset = self.get_offset_to_xy(x, y);
|
||||||
Some(self.pixels.as_mut_ptr().add(offset))
|
Some(self.pixels.as_mut_ptr().add(offset))
|
||||||
|
@ -270,7 +252,7 @@ impl Bitmap {
|
||||||
/// given coordinates. The coordinates are not checked for validity, so it is up to you to
|
/// 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.
|
/// ensure they lie within the bounds of the bitmap.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn pixels_at_ptr_unchecked(&self, x: i32, y: i32) -> *const u8 {
|
pub unsafe fn pixels_at_ptr_unchecked(&self, x: i32, y: i32) -> *const PixelType {
|
||||||
let offset = self.get_offset_to_xy(x, y);
|
let offset = self.get_offset_to_xy(x, y);
|
||||||
self.pixels.as_ptr().add(offset)
|
self.pixels.as_ptr().add(offset)
|
||||||
}
|
}
|
||||||
|
@ -279,7 +261,7 @@ impl Bitmap {
|
||||||
/// at the given coordinates. The coordinates are not checked for validity, so it is up to you
|
/// 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.
|
/// to ensure they lie within the bounds of the bitmap.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn pixels_at_mut_ptr_unchecked(&mut self, x: i32, y: i32) -> *mut u8 {
|
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);
|
let offset = self.get_offset_to_xy(x, y);
|
||||||
self.pixels.as_mut_ptr().add(offset)
|
self.pixels.as_mut_ptr().add(offset)
|
||||||
}
|
}
|
||||||
|
@ -300,20 +282,6 @@ impl Bitmap {
|
||||||
&& (x <= self.clip_region.right())
|
&& (x <= self.clip_region.right())
|
||||||
&& (y <= self.clip_region.bottom())
|
&& (y <= self.clip_region.bottom())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Copies and converts the entire pixel data from this bitmap to a destination expecting
|
|
||||||
/// 32-bit ARGB-format pixel data. This can be used to display the contents of the bitmap
|
|
||||||
/// on-screen by using an SDL Surface, OpenGL texture, etc as the destination.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `dest`: destination 32-bit ARGB pixel buffer to copy converted pixels to
|
|
||||||
/// * `palette`: the 256 colour palette to use during pixel conversion
|
|
||||||
pub fn copy_as_argb_to(&self, dest: &mut [u32], palette: &Palette) {
|
|
||||||
for (src, dest) in self.pixels().iter().zip(dest.iter_mut()) {
|
|
||||||
*dest = palette[*src];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -344,10 +312,10 @@ pub mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn creation_and_sizing() {
|
pub fn creation_and_sizing() {
|
||||||
assert_matches!(Bitmap::new(0, 0), Err(BitmapError::InvalidDimensions));
|
assert_matches!(Bitmap::<u8>::new(0, 0), Err(BitmapError::InvalidDimensions));
|
||||||
assert_matches!(Bitmap::new(16, 0), Err(BitmapError::InvalidDimensions));
|
assert_matches!(Bitmap::<u8>::new(16, 0), Err(BitmapError::InvalidDimensions));
|
||||||
assert_matches!(Bitmap::new(0, 32), Err(BitmapError::InvalidDimensions));
|
assert_matches!(Bitmap::<u8>::new(0, 32), Err(BitmapError::InvalidDimensions));
|
||||||
let bmp = Bitmap::new(16, 32).unwrap();
|
let bmp = Bitmap::<u8>::new(16, 32).unwrap();
|
||||||
assert_eq!(16, bmp.width());
|
assert_eq!(16, bmp.width());
|
||||||
assert_eq!(32, bmp.height());
|
assert_eq!(32, bmp.height());
|
||||||
assert_eq!(15, bmp.right());
|
assert_eq!(15, bmp.right());
|
||||||
|
@ -374,24 +342,24 @@ pub mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn copy_from() {
|
pub fn copy_from() {
|
||||||
let mut bmp = Bitmap::new(8, 8).unwrap();
|
let mut bmp = Bitmap::<u8>::new(8, 8).unwrap();
|
||||||
bmp.pixels_mut().copy_from_slice(RAW_BMP_PIXELS);
|
bmp.pixels_mut().copy_from_slice(RAW_BMP_PIXELS);
|
||||||
|
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
Bitmap::from(&bmp, &Rect::new(0, 0, 16, 16)),
|
Bitmap::<u8>::from(&bmp, &Rect::new(0, 0, 16, 16)),
|
||||||
Err(BitmapError::OutOfBounds)
|
Err(BitmapError::OutOfBounds)
|
||||||
);
|
);
|
||||||
|
|
||||||
let copy = Bitmap::from(&bmp, &Rect::new(0, 0, 8, 8)).unwrap();
|
let copy = Bitmap::<u8>::from(&bmp, &Rect::new(0, 0, 8, 8)).unwrap();
|
||||||
assert_eq!(bmp.pixels(), copy.pixels());
|
assert_eq!(bmp.pixels(), copy.pixels());
|
||||||
|
|
||||||
let copy = Bitmap::from(&bmp, &Rect::new(4, 4, 4, 4)).unwrap();
|
let copy = Bitmap::<u8>::from(&bmp, &Rect::new(4, 4, 4, 4)).unwrap();
|
||||||
assert_eq!(RAW_BMP_PIXELS_SUBSET, copy.pixels());
|
assert_eq!(RAW_BMP_PIXELS_SUBSET, copy.pixels());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn xy_offset_calculation() {
|
pub fn xy_offset_calculation() {
|
||||||
let bmp = Bitmap::new(20, 15).unwrap();
|
let bmp = Bitmap::<u8>::new(20, 15).unwrap();
|
||||||
assert_eq!(0, bmp.get_offset_to_xy(0, 0));
|
assert_eq!(0, bmp.get_offset_to_xy(0, 0));
|
||||||
assert_eq!(19, bmp.get_offset_to_xy(19, 0));
|
assert_eq!(19, bmp.get_offset_to_xy(19, 0));
|
||||||
assert_eq!(20, bmp.get_offset_to_xy(0, 1));
|
assert_eq!(20, bmp.get_offset_to_xy(0, 1));
|
||||||
|
@ -402,7 +370,7 @@ pub mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn bounds_testing_and_clip_region() {
|
pub fn bounds_testing_and_clip_region() {
|
||||||
let mut bmp = Bitmap::new(16, 8).unwrap();
|
let mut bmp = Bitmap::<u8>::new(16, 8).unwrap();
|
||||||
assert!(bmp.is_xy_visible(0, 0));
|
assert!(bmp.is_xy_visible(0, 0));
|
||||||
assert!(bmp.is_xy_visible(15, 0));
|
assert!(bmp.is_xy_visible(15, 0));
|
||||||
assert!(bmp.is_xy_visible(0, 7));
|
assert!(bmp.is_xy_visible(0, 7));
|
||||||
|
@ -452,7 +420,7 @@ pub mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn pixels_at() {
|
pub fn pixels_at() {
|
||||||
let mut bmp = Bitmap::new(8, 8).unwrap();
|
let mut bmp = Bitmap::<u8>::new(8, 8).unwrap();
|
||||||
bmp.pixels_mut().copy_from_slice(RAW_BMP_PIXELS);
|
bmp.pixels_mut().copy_from_slice(RAW_BMP_PIXELS);
|
||||||
|
|
||||||
assert_eq!(None, bmp.pixels_at(-1, -1));
|
assert_eq!(None, bmp.pixels_at(-1, -1));
|
||||||
|
@ -472,7 +440,7 @@ pub mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn pixels_at_mut() {
|
pub fn pixels_at_mut() {
|
||||||
let mut bmp = Bitmap::new(8, 8).unwrap();
|
let mut bmp = Bitmap::<u8>::new(8, 8).unwrap();
|
||||||
bmp.pixels_mut().copy_from_slice(RAW_BMP_PIXELS);
|
bmp.pixels_mut().copy_from_slice(RAW_BMP_PIXELS);
|
||||||
|
|
||||||
assert_eq!(None, bmp.pixels_at_mut(-1, -1));
|
assert_eq!(None, bmp.pixels_at_mut(-1, -1));
|
||||||
|
@ -492,7 +460,7 @@ pub mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn pixels_at_unchecked() {
|
pub fn pixels_at_unchecked() {
|
||||||
let mut bmp = Bitmap::new(8, 8).unwrap();
|
let mut bmp = Bitmap::<u8>::new(8, 8).unwrap();
|
||||||
bmp.pixels_mut().copy_from_slice(RAW_BMP_PIXELS);
|
bmp.pixels_mut().copy_from_slice(RAW_BMP_PIXELS);
|
||||||
|
|
||||||
let offset = bmp.get_offset_to_xy(1, 1);
|
let offset = bmp.get_offset_to_xy(1, 1);
|
||||||
|
@ -510,7 +478,7 @@ pub mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn pixels_at_mut_unchecked() {
|
pub fn pixels_at_mut_unchecked() {
|
||||||
let mut bmp = Bitmap::new(8, 8).unwrap();
|
let mut bmp = Bitmap::<u8>::new(8, 8).unwrap();
|
||||||
bmp.pixels_mut().copy_from_slice(RAW_BMP_PIXELS);
|
bmp.pixels_mut().copy_from_slice(RAW_BMP_PIXELS);
|
||||||
|
|
||||||
let offset = bmp.get_offset_to_xy(1, 1);
|
let offset = bmp.get_offset_to_xy(1, 1);
|
||||||
|
@ -528,7 +496,7 @@ pub mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn pixels_at_ptr() {
|
pub fn pixels_at_ptr() {
|
||||||
let mut bmp = Bitmap::new(8, 8).unwrap();
|
let mut bmp = Bitmap::<u8>::new(8, 8).unwrap();
|
||||||
bmp.pixels_mut().copy_from_slice(RAW_BMP_PIXELS);
|
bmp.pixels_mut().copy_from_slice(RAW_BMP_PIXELS);
|
||||||
|
|
||||||
assert_eq!(None, unsafe { bmp.pixels_at_ptr(-1, -1) });
|
assert_eq!(None, unsafe { bmp.pixels_at_ptr(-1, -1) });
|
||||||
|
@ -546,7 +514,7 @@ pub mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn pixels_at_mut_ptr() {
|
pub fn pixels_at_mut_ptr() {
|
||||||
let mut bmp = Bitmap::new(8, 8).unwrap();
|
let mut bmp = Bitmap::<u8>::new(8, 8).unwrap();
|
||||||
bmp.pixels_mut().copy_from_slice(RAW_BMP_PIXELS);
|
bmp.pixels_mut().copy_from_slice(RAW_BMP_PIXELS);
|
||||||
|
|
||||||
assert_eq!(None, unsafe { bmp.pixels_at_mut_ptr(-1, -1) });
|
assert_eq!(None, unsafe { bmp.pixels_at_mut_ptr(-1, -1) });
|
||||||
|
@ -564,7 +532,7 @@ pub mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn pixels_at_ptr_unchecked() {
|
pub fn pixels_at_ptr_unchecked() {
|
||||||
let mut bmp = Bitmap::new(8, 8).unwrap();
|
let mut bmp = Bitmap::<u8>::new(8, 8).unwrap();
|
||||||
bmp.pixels_mut().copy_from_slice(RAW_BMP_PIXELS);
|
bmp.pixels_mut().copy_from_slice(RAW_BMP_PIXELS);
|
||||||
|
|
||||||
let offset = bmp.get_offset_to_xy(1, 1);
|
let offset = bmp.get_offset_to_xy(1, 1);
|
||||||
|
@ -580,7 +548,7 @@ pub mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn pixels_at_mut_ptr_unchecked() {
|
pub fn pixels_at_mut_ptr_unchecked() {
|
||||||
let mut bmp = Bitmap::new(8, 8).unwrap();
|
let mut bmp = Bitmap::<u8>::new(8, 8).unwrap();
|
||||||
bmp.pixels_mut().copy_from_slice(RAW_BMP_PIXELS);
|
bmp.pixels_mut().copy_from_slice(RAW_BMP_PIXELS);
|
||||||
|
|
||||||
let offset = bmp.get_offset_to_xy(1, 1);
|
let offset = bmp.get_offset_to_xy(1, 1);
|
|
@ -5,9 +5,9 @@ use std::path::Path;
|
||||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use crate::graphics::bitmap::indexed::IndexedBitmap;
|
||||||
use crate::graphics::color::from_rgb32;
|
use crate::graphics::color::from_rgb32;
|
||||||
use crate::graphics::indexed::bitmap::Bitmap;
|
use crate::graphics::palette::{Palette, PaletteError, PaletteFormat};
|
||||||
use crate::graphics::indexed::palette::{Palette, PaletteError, PaletteFormat};
|
|
||||||
use crate::utils::bytes::ReadFixedLengthByteArray;
|
use crate::utils::bytes::ReadFixedLengthByteArray;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
|
@ -104,10 +104,10 @@ fn write_pcx_data<T: WriteBytesExt>(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Bitmap {
|
impl IndexedBitmap {
|
||||||
pub fn load_pcx_bytes<T: ReadBytesExt + Seek>(
|
pub fn load_pcx_bytes<T: ReadBytesExt + Seek>(
|
||||||
reader: &mut T,
|
reader: &mut T,
|
||||||
) -> Result<(Bitmap, Palette), PcxError> {
|
) -> Result<(IndexedBitmap, Palette), PcxError> {
|
||||||
let header = PcxHeader::read(reader)?;
|
let header = PcxHeader::read(reader)?;
|
||||||
|
|
||||||
if header.manufacturer != 10 {
|
if header.manufacturer != 10 {
|
||||||
|
@ -140,7 +140,7 @@ impl Bitmap {
|
||||||
|
|
||||||
let width = (header.x2 + 1) as u32;
|
let width = (header.x2 + 1) as u32;
|
||||||
let height = (header.y2 + 1) as u32;
|
let height = (header.y2 + 1) as u32;
|
||||||
let mut bmp = Bitmap::new(width, height).unwrap();
|
let mut bmp = IndexedBitmap::new(width, height).unwrap();
|
||||||
let mut writer = Cursor::new(bmp.pixels_mut());
|
let mut writer = Cursor::new(bmp.pixels_mut());
|
||||||
|
|
||||||
for _y in 0..height {
|
for _y in 0..height {
|
||||||
|
@ -194,7 +194,7 @@ impl Bitmap {
|
||||||
Ok((bmp, palette))
|
Ok((bmp, palette))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_pcx_file(path: &Path) -> Result<(Bitmap, Palette), PcxError> {
|
pub fn load_pcx_file(path: &Path) -> Result<(IndexedBitmap, Palette), PcxError> {
|
||||||
let f = File::open(path)?;
|
let f = File::open(path)?;
|
||||||
let mut reader = BufReader::new(f);
|
let mut reader = BufReader::new(f);
|
||||||
Self::load_pcx_bytes(&mut reader)
|
Self::load_pcx_bytes(&mut reader)
|
||||||
|
@ -286,9 +286,9 @@ pub mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub static TEST_BMP_PIXELS_RAW: &[u8] = include_bytes!("../../../../test-assets/test_bmp_pixels_raw.bin");
|
pub static TEST_BMP_PIXELS_RAW: &[u8] = include_bytes!("../../../test-assets/test_bmp_pixels_raw.bin");
|
||||||
pub static TEST_LARGE_BMP_PIXELS_RAW: &[u8] = include_bytes!("../../../../test-assets/test_large_bmp_pixels_raw.bin");
|
pub static TEST_LARGE_BMP_PIXELS_RAW: &[u8] = include_bytes!("../../../test-assets/test_large_bmp_pixels_raw.bin");
|
||||||
pub static TEST_LARGE_BMP_PIXELS_RAW_2: &[u8] = include_bytes!("../../../../test-assets/test_large_bmp_pixels_raw2.bin");
|
pub static TEST_LARGE_BMP_PIXELS_RAW_2: &[u8] = include_bytes!("../../../test-assets/test_large_bmp_pixels_raw2.bin");
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn load_and_save() -> Result<(), PcxError> {
|
pub fn load_and_save() -> Result<(), PcxError> {
|
||||||
|
@ -297,7 +297,7 @@ pub mod tests {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let tmp_dir = TempDir::new()?;
|
let tmp_dir = TempDir::new()?;
|
||||||
|
|
||||||
let (bmp, palette) = Bitmap::load_pcx_file(Path::new("./test-assets/test.pcx"))?;
|
let (bmp, palette) = IndexedBitmap::load_pcx_file(Path::new("./test-assets/test.pcx"))?;
|
||||||
assert_eq!(16, bmp.width());
|
assert_eq!(16, bmp.width());
|
||||||
assert_eq!(16, bmp.height());
|
assert_eq!(16, bmp.height());
|
||||||
assert_eq!(bmp.pixels(), TEST_BMP_PIXELS_RAW);
|
assert_eq!(bmp.pixels(), TEST_BMP_PIXELS_RAW);
|
||||||
|
@ -305,7 +305,7 @@ pub mod tests {
|
||||||
|
|
||||||
let save_path = tmp_dir.path().join("test_save.pcx");
|
let save_path = tmp_dir.path().join("test_save.pcx");
|
||||||
bmp.to_pcx_file(&save_path, &palette)?;
|
bmp.to_pcx_file(&save_path, &palette)?;
|
||||||
let (reloaded_bmp, reloaded_palette) = Bitmap::load_pcx_file(&save_path)?;
|
let (reloaded_bmp, reloaded_palette) = IndexedBitmap::load_pcx_file(&save_path)?;
|
||||||
assert_eq!(16, reloaded_bmp.width());
|
assert_eq!(16, reloaded_bmp.width());
|
||||||
assert_eq!(16, reloaded_bmp.height());
|
assert_eq!(16, reloaded_bmp.height());
|
||||||
assert_eq!(reloaded_bmp.pixels(), TEST_BMP_PIXELS_RAW);
|
assert_eq!(reloaded_bmp.pixels(), TEST_BMP_PIXELS_RAW);
|
||||||
|
@ -320,28 +320,28 @@ pub mod tests {
|
||||||
|
|
||||||
// first image
|
// first image
|
||||||
|
|
||||||
let (bmp, palette) = Bitmap::load_pcx_file(Path::new("./test-assets/test_image.pcx"))?;
|
let (bmp, palette) = IndexedBitmap::load_pcx_file(Path::new("./test-assets/test_image.pcx"))?;
|
||||||
assert_eq!(320, bmp.width());
|
assert_eq!(320, bmp.width());
|
||||||
assert_eq!(200, bmp.height());
|
assert_eq!(200, bmp.height());
|
||||||
assert_eq!(bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW);
|
assert_eq!(bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW);
|
||||||
|
|
||||||
let save_path = tmp_dir.path().join("test_save.pcx");
|
let save_path = tmp_dir.path().join("test_save.pcx");
|
||||||
bmp.to_pcx_file(&save_path, &palette)?;
|
bmp.to_pcx_file(&save_path, &palette)?;
|
||||||
let (reloaded_bmp, _) = Bitmap::load_pcx_file(&save_path)?;
|
let (reloaded_bmp, _) = IndexedBitmap::load_pcx_file(&save_path)?;
|
||||||
assert_eq!(320, reloaded_bmp.width());
|
assert_eq!(320, reloaded_bmp.width());
|
||||||
assert_eq!(200, reloaded_bmp.height());
|
assert_eq!(200, reloaded_bmp.height());
|
||||||
assert_eq!(reloaded_bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW);
|
assert_eq!(reloaded_bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW);
|
||||||
|
|
||||||
// second image
|
// second image
|
||||||
|
|
||||||
let (bmp, palette) = Bitmap::load_pcx_file(Path::new("./test-assets/test_image2.pcx"))?;
|
let (bmp, palette) = IndexedBitmap::load_pcx_file(Path::new("./test-assets/test_image2.pcx"))?;
|
||||||
assert_eq!(320, bmp.width());
|
assert_eq!(320, bmp.width());
|
||||||
assert_eq!(200, bmp.height());
|
assert_eq!(200, bmp.height());
|
||||||
assert_eq!(bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW_2);
|
assert_eq!(bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW_2);
|
||||||
|
|
||||||
let save_path = tmp_dir.path().join("test_save_2.pcx");
|
let save_path = tmp_dir.path().join("test_save_2.pcx");
|
||||||
bmp.to_pcx_file(&save_path, &palette)?;
|
bmp.to_pcx_file(&save_path, &palette)?;
|
||||||
let (reloaded_bmp, _) = Bitmap::load_pcx_file(&save_path)?;
|
let (reloaded_bmp, _) = IndexedBitmap::load_pcx_file(&save_path)?;
|
||||||
assert_eq!(320, reloaded_bmp.width());
|
assert_eq!(320, reloaded_bmp.width());
|
||||||
assert_eq!(200, reloaded_bmp.height());
|
assert_eq!(200, reloaded_bmp.height());
|
||||||
assert_eq!(reloaded_bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW_2);
|
assert_eq!(reloaded_bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW_2);
|
392
ggdt/src/graphics/bitmap/primitives.rs
Normal file
392
ggdt/src/graphics/bitmap/primitives.rs
Normal file
|
@ -0,0 +1,392 @@
|
||||||
|
use std::mem::swap;
|
||||||
|
|
||||||
|
use crate::graphics::bitmap::Bitmap;
|
||||||
|
use crate::graphics::font::{Character, Font, FontRenderOpts};
|
||||||
|
use crate::graphics::Pixel;
|
||||||
|
use crate::math::rect::Rect;
|
||||||
|
|
||||||
|
impl<PixelType: Pixel> Bitmap<PixelType> {
|
||||||
|
/// Fills the entire bitmap with the given color.
|
||||||
|
pub fn clear(&mut self, color: PixelType) {
|
||||||
|
self.pixels.fill(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the pixel at the given coordinates to the color specified. If the coordinates lie
|
||||||
|
/// outside of the bitmaps clipping region, no pixels will be changed.
|
||||||
|
#[inline]
|
||||||
|
pub fn set_pixel(&mut self, x: i32, y: i32, color: PixelType) {
|
||||||
|
if let Some(pixels) = self.pixels_at_mut(x, y) {
|
||||||
|
pixels[0] = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the pixel at the given coordinates to the color specified. The coordinates are not
|
||||||
|
/// checked for validity, so it is up to you to ensure they lie within the bounds of the
|
||||||
|
/// bitmap.
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn set_pixel_unchecked(&mut self, x: i32, y: i32, color: PixelType) {
|
||||||
|
let p = self.pixels_at_mut_ptr_unchecked(x, y);
|
||||||
|
*p = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the pixel at the given coordinates. If the coordinates lie outside of the bitmaps
|
||||||
|
/// clipping region, None is returned.
|
||||||
|
#[inline]
|
||||||
|
pub fn get_pixel(&self, x: i32, y: i32) -> Option<PixelType> {
|
||||||
|
if let Some(pixels) = self.pixels_at(x, y) {
|
||||||
|
Some(pixels[0])
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the pixel at the given coordinates. The coordinates are not checked for validity, so
|
||||||
|
/// it is up to you to ensure they lie within the bounds of the bitmap.
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn get_pixel_unchecked(&self, x: i32, y: i32) -> PixelType {
|
||||||
|
*(self.pixels_at_ptr_unchecked(x, y))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Renders a single character using the font given.
|
||||||
|
#[inline]
|
||||||
|
pub fn print_char<T: Font>(&mut self, ch: char, x: i32, y: i32, opts: FontRenderOpts<PixelType>, font: &T) {
|
||||||
|
font.character(ch)
|
||||||
|
.draw(self, x, y, opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Renders the string of text using the font given.
|
||||||
|
pub fn print_string<T: Font>(&mut self, text: &str, x: i32, y: i32, opts: FontRenderOpts<PixelType>, font: &T) {
|
||||||
|
let mut current_x = x;
|
||||||
|
let mut current_y = y;
|
||||||
|
for ch in text.chars() {
|
||||||
|
match ch {
|
||||||
|
' ' => current_x += font.space_width() as i32,
|
||||||
|
'\n' => {
|
||||||
|
current_x = x;
|
||||||
|
current_y += font.line_height() as i32
|
||||||
|
}
|
||||||
|
'\r' => (),
|
||||||
|
otherwise => {
|
||||||
|
self.print_char(otherwise, current_x, current_y, opts, font);
|
||||||
|
current_x += font.character(otherwise).bounds().width as i32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draws a line from x1,y1 to x2,y2.
|
||||||
|
pub fn line(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: PixelType) {
|
||||||
|
let mut dx = x1;
|
||||||
|
let mut dy = y1;
|
||||||
|
let delta_x = x2 - x1;
|
||||||
|
let delta_y = y2 - y1;
|
||||||
|
let delta_x_abs = delta_x.abs();
|
||||||
|
let delta_y_abs = delta_y.abs();
|
||||||
|
let delta_x_sign = delta_x.signum();
|
||||||
|
let delta_y_sign = delta_y.signum();
|
||||||
|
let mut x = delta_x_abs / 2;
|
||||||
|
let mut y = delta_y_abs / 2;
|
||||||
|
let offset_x_inc = delta_x_sign;
|
||||||
|
let offset_y_inc = delta_y_sign * self.width as i32;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
// safety: while we are blindly getting a pointer to this x/y coordinate, we don't
|
||||||
|
// write to it unless we know the coordinates are in bounds.
|
||||||
|
// TODO: should be ok ... ? or am i making too many assumptions about memory layout?
|
||||||
|
let mut dest = self.pixels_at_mut_ptr_unchecked(x1, y1);
|
||||||
|
|
||||||
|
if self.is_xy_visible(dx, dy) {
|
||||||
|
*dest = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
if delta_x_abs >= delta_y_abs {
|
||||||
|
for _ in 0..delta_x_abs {
|
||||||
|
y += delta_y_abs;
|
||||||
|
|
||||||
|
if y >= delta_x_abs {
|
||||||
|
y -= delta_x_abs;
|
||||||
|
dy += delta_y_sign;
|
||||||
|
dest = dest.offset(offset_y_inc as isize);
|
||||||
|
}
|
||||||
|
|
||||||
|
dx += delta_x_sign;
|
||||||
|
dest = dest.offset(offset_x_inc as isize);
|
||||||
|
|
||||||
|
if self.is_xy_visible(dx, dy) {
|
||||||
|
*dest = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for _ in 0..delta_y_abs {
|
||||||
|
x += delta_x_abs;
|
||||||
|
|
||||||
|
if x >= delta_y_abs {
|
||||||
|
x -= delta_y_abs;
|
||||||
|
dx += delta_x_sign;
|
||||||
|
dest = dest.offset(offset_x_inc as isize);
|
||||||
|
}
|
||||||
|
|
||||||
|
dy += delta_y_sign;
|
||||||
|
dest = dest.offset(offset_y_inc as isize);
|
||||||
|
|
||||||
|
if self.is_xy_visible(dx, dy) {
|
||||||
|
*dest = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draws a horizontal line from x1,y to x2,y.
|
||||||
|
pub fn horiz_line(&mut self, x1: i32, x2: i32, y: i32, color: PixelType) {
|
||||||
|
let mut region = Rect::from_coords(x1, y, x2, y);
|
||||||
|
if region.clamp_to(&self.clip_region) {
|
||||||
|
unsafe {
|
||||||
|
let dest = &mut self.pixels_at_mut_unchecked(region.x, region.y)[0..region.width as usize];
|
||||||
|
for pixel in dest.iter_mut() {
|
||||||
|
*pixel = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draws a vertical line from x,y1 to x,y2.
|
||||||
|
pub fn vert_line(&mut self, x: i32, y1: i32, y2: i32, color: PixelType) {
|
||||||
|
let mut region = Rect::from_coords(x, y1, x, y2);
|
||||||
|
if region.clamp_to(&self.clip_region) {
|
||||||
|
unsafe {
|
||||||
|
let mut dest = self.pixels_at_mut_ptr_unchecked(region.x, region.y);
|
||||||
|
for _ in 0..region.height {
|
||||||
|
*dest = color;
|
||||||
|
dest = dest.add(self.width as usize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draws an empty box (rectangle) using the points x1,y1 and x2,y2 to form the box to be
|
||||||
|
/// drawn, assuming they are specifying the top-left and bottom-right corners respectively.
|
||||||
|
pub fn rect(&mut self, mut x1: i32, mut y1: i32, mut x2: i32, mut y2: i32, color: PixelType) {
|
||||||
|
// note: need to manually do all this instead of just relying on Rect::from_coords (which
|
||||||
|
// could otherwise figure all this out for us) mainly just because we need the post-swap
|
||||||
|
// x1,y1,x2,y2 values for post-region-clamping comparison purposes ...
|
||||||
|
if x2 < x1 {
|
||||||
|
swap(&mut x1, &mut x2);
|
||||||
|
}
|
||||||
|
if y2 < y1 {
|
||||||
|
swap(&mut y1, &mut y2);
|
||||||
|
}
|
||||||
|
let mut region = Rect {
|
||||||
|
x: x1,
|
||||||
|
y: y1,
|
||||||
|
width: (x2 - x1 + 1) as u32,
|
||||||
|
height: (y2 - y1 + 1) as u32,
|
||||||
|
};
|
||||||
|
if !region.clamp_to(&self.clip_region) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// top line, only if y1 was originally within bounds
|
||||||
|
if y1 == region.y {
|
||||||
|
unsafe {
|
||||||
|
let dest = &mut self.pixels_at_mut_unchecked(region.x, region.y)[0..region.width as usize];
|
||||||
|
for pixel in dest.iter_mut() {
|
||||||
|
*pixel = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// bottom line, only if y2 was originally within bounds
|
||||||
|
if y2 == region.bottom() {
|
||||||
|
unsafe {
|
||||||
|
let dest = &mut self.pixels_at_mut_unchecked(region.x, region.bottom())[0..region.width as usize];
|
||||||
|
for pixel in dest.iter_mut() {
|
||||||
|
*pixel = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// left line, only if x1 was originally within bounds
|
||||||
|
if x1 == region.x {
|
||||||
|
unsafe {
|
||||||
|
let mut dest = self.pixels_at_mut_ptr_unchecked(region.x, region.y);
|
||||||
|
for _ in 0..region.height {
|
||||||
|
*dest = color;
|
||||||
|
dest = dest.add(self.width as usize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// right line, only if x2 was originally within bounds
|
||||||
|
if x2 == region.right() {
|
||||||
|
unsafe {
|
||||||
|
let mut dest = self.pixels_at_mut_ptr_unchecked(region.right(), region.y);
|
||||||
|
for _ in 0..region.height {
|
||||||
|
*dest = color;
|
||||||
|
dest = dest.add(self.width as usize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draws a filled box (rectangle) using the points x1,y1 and x2,y2 to form the box to be
|
||||||
|
/// drawn, assuming they are specifying the top-left and bottom-right corners respectively.
|
||||||
|
pub fn filled_rect(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: PixelType) {
|
||||||
|
let mut region = Rect::from_coords(x1, y1, x2, y2);
|
||||||
|
if region.clamp_to(&self.clip_region) {
|
||||||
|
unsafe {
|
||||||
|
let mut dest = self.pixels_at_mut_ptr_unchecked(region.x, region.y);
|
||||||
|
for _ in 0..region.height {
|
||||||
|
// write the pixels horizontally for the entire width
|
||||||
|
for x in 0..region.width as isize {
|
||||||
|
let dest_x = dest.offset(x);
|
||||||
|
*dest_x = color;
|
||||||
|
}
|
||||||
|
// move original pointer to the next row
|
||||||
|
dest = dest.add(self.width as usize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draws the outline of a circle formed by the center point and radius given.
|
||||||
|
pub fn circle(&mut self, center_x: i32, center_y: i32, radius: u32, color: PixelType) {
|
||||||
|
// TODO: optimize
|
||||||
|
let mut x = 0;
|
||||||
|
let mut y = radius as i32;
|
||||||
|
let mut m = 5 - 4 * radius as i32;
|
||||||
|
|
||||||
|
while x <= y {
|
||||||
|
self.set_pixel(center_x + x, center_y + y, color);
|
||||||
|
self.set_pixel(center_x + x, center_y - y, color);
|
||||||
|
self.set_pixel(center_x - x, center_y + y, color);
|
||||||
|
self.set_pixel(center_x - x, center_y - y, color);
|
||||||
|
self.set_pixel(center_x + y, center_y + x, color);
|
||||||
|
self.set_pixel(center_x + y, center_y - x, color);
|
||||||
|
self.set_pixel(center_x - y, center_y + x, color);
|
||||||
|
self.set_pixel(center_x - y, center_y - x, color);
|
||||||
|
|
||||||
|
if m > 0 {
|
||||||
|
y -= 1;
|
||||||
|
m -= 8 * y;
|
||||||
|
}
|
||||||
|
|
||||||
|
x += 1;
|
||||||
|
m += 8 * x + 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draws a filled circle formed by the center point and radius given.
|
||||||
|
pub fn filled_circle(&mut self, center_x: i32, center_y: i32, radius: u32, color: PixelType) {
|
||||||
|
// TODO: optimize
|
||||||
|
let mut x = 0;
|
||||||
|
let mut y = radius as i32;
|
||||||
|
let mut m = 5 - 4 * radius as i32;
|
||||||
|
|
||||||
|
while x <= y {
|
||||||
|
self.horiz_line(center_x - x, center_x + x, center_y - y, color);
|
||||||
|
self.horiz_line(center_x - y, center_x + y, center_y - x, color);
|
||||||
|
self.horiz_line(center_x - y, center_x + y, center_y + x, color);
|
||||||
|
self.horiz_line(center_x - x, center_x + x, center_y + y, color);
|
||||||
|
|
||||||
|
if m > 0 {
|
||||||
|
y -= 1;
|
||||||
|
m -= 8 * y;
|
||||||
|
}
|
||||||
|
|
||||||
|
x += 1;
|
||||||
|
m += 8 * x + 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
#[test]
|
||||||
|
pub fn set_and_get_pixel() {
|
||||||
|
let mut bmp = Bitmap::<u8>::new(8, 8).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(None, bmp.get_pixel(-1, -1));
|
||||||
|
|
||||||
|
assert_eq!(0, bmp.get_pixel(0, 0).unwrap());
|
||||||
|
bmp.set_pixel(0, 0, 7);
|
||||||
|
assert_eq!(7, bmp.get_pixel(0, 0).unwrap());
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
bmp.pixels(),
|
||||||
|
&[
|
||||||
|
7, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(0, bmp.get_pixel(2, 4).unwrap());
|
||||||
|
bmp.set_pixel(2, 4, 5);
|
||||||
|
assert_eq!(5, bmp.get_pixel(2, 4).unwrap());
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
bmp.pixels(),
|
||||||
|
&[
|
||||||
|
7, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 5, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
#[test]
|
||||||
|
pub fn set_and_get_pixel_unchecked() {
|
||||||
|
let mut bmp = Bitmap::<u8>::new(8, 8).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(0, unsafe { bmp.get_pixel_unchecked(0, 0) });
|
||||||
|
unsafe { bmp.set_pixel_unchecked(0, 0, 7) };
|
||||||
|
assert_eq!(7, unsafe { bmp.get_pixel_unchecked(0, 0) });
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
bmp.pixels(),
|
||||||
|
&[
|
||||||
|
7, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(0, unsafe { bmp.get_pixel_unchecked(2, 4) });
|
||||||
|
unsafe { bmp.set_pixel_unchecked(2, 4, 5) };
|
||||||
|
assert_eq!(5, unsafe { bmp.get_pixel_unchecked(2, 4) });
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
bmp.pixels(),
|
||||||
|
&[
|
||||||
|
7, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 5, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
175
ggdt/src/graphics/bitmap/rgb/blit.rs
Normal file
175
ggdt/src/graphics/bitmap/rgb/blit.rs
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
use crate::graphics::bitmap::blit::clip_blit;
|
||||||
|
use crate::graphics::bitmap::rgb::RgbaBitmap;
|
||||||
|
use crate::graphics::bitmapatlas::BitmapAtlas;
|
||||||
|
use crate::math::rect::Rect;
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq)]
|
||||||
|
pub enum RgbaBlitMethod {
|
||||||
|
/// Solid blit, no transparency or other per-pixel adjustments.
|
||||||
|
Solid,
|
||||||
|
/// Same as [RgbaBlitMethod::Solid] but the drawn image can also be flipped horizontally
|
||||||
|
/// and/or vertically.
|
||||||
|
SolidFlipped {
|
||||||
|
horizontal_flip: bool,
|
||||||
|
vertical_flip: bool,
|
||||||
|
},
|
||||||
|
/// Transparent blit, the specified source color pixels are skipped.
|
||||||
|
Transparent(u32),
|
||||||
|
/// Same as [RgbaBlitMethod::Transparent] but the drawn image can also be flipped horizontally
|
||||||
|
/// and/or vertically.
|
||||||
|
TransparentFlipped {
|
||||||
|
transparent_color: u32,
|
||||||
|
horizontal_flip: bool,
|
||||||
|
vertical_flip: bool,
|
||||||
|
},
|
||||||
|
/// 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,
|
||||||
|
},
|
||||||
|
/// Combination of [RgbaBlitMethod::TransparentFlipped] and [RgbaBlitMethod::TransparentSingle].
|
||||||
|
TransparentFlippedSingle {
|
||||||
|
transparent_color: u32,
|
||||||
|
horizontal_flip: bool,
|
||||||
|
vertical_flip: bool,
|
||||||
|
draw_color: u32,
|
||||||
|
},
|
||||||
|
/// Rotozoom blit, works the same as [RgbaBlitMethod::Solid] except that rotation and scaling is
|
||||||
|
/// performed.
|
||||||
|
RotoZoom {
|
||||||
|
angle: f32,
|
||||||
|
scale_x: f32,
|
||||||
|
scale_y: f32,
|
||||||
|
},
|
||||||
|
/// Same as [RgbaBlitMethod::RotoZoom] except that the specified source color pixels are skipped.
|
||||||
|
RotoZoomTransparent {
|
||||||
|
angle: f32,
|
||||||
|
scale_x: f32,
|
||||||
|
scale_y: f32,
|
||||||
|
transparent_color: u32,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RgbaBitmap {
|
||||||
|
pub fn blit_region(
|
||||||
|
&mut self,
|
||||||
|
method: RgbaBlitMethod,
|
||||||
|
src: &Self,
|
||||||
|
src_region: &Rect,
|
||||||
|
mut dest_x: i32,
|
||||||
|
mut dest_y: i32,
|
||||||
|
) {
|
||||||
|
// make sure the source region is clipped or even valid at all for the source bitmap given
|
||||||
|
let mut src_region = *src_region;
|
||||||
|
if !src_region.clamp_to(&src.clip_region) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// some blit methods need to handle clipping a bit differently than others
|
||||||
|
use RgbaBlitMethod::*;
|
||||||
|
match method {
|
||||||
|
// rotozoom blits internally clip per-pixel right now ... and regardless, the normal
|
||||||
|
// clip_blit() function wouldn't handle a rotozoom blit destination region anyway ...
|
||||||
|
RotoZoom { .. } => {}
|
||||||
|
RotoZoomTransparent { .. } => {}
|
||||||
|
|
||||||
|
// set axis flip arguments
|
||||||
|
SolidFlipped { horizontal_flip, vertical_flip, .. } |
|
||||||
|
TransparentFlipped { horizontal_flip, vertical_flip, .. } |
|
||||||
|
TransparentFlippedSingle { horizontal_flip, vertical_flip, .. } => {
|
||||||
|
if !clip_blit(
|
||||||
|
self.clip_region(),
|
||||||
|
&mut src_region,
|
||||||
|
&mut dest_x,
|
||||||
|
&mut dest_y,
|
||||||
|
horizontal_flip,
|
||||||
|
vertical_flip,
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise clip like normal!
|
||||||
|
_ => {
|
||||||
|
if !clip_blit(
|
||||||
|
self.clip_region(),
|
||||||
|
&mut src_region,
|
||||||
|
&mut dest_x,
|
||||||
|
&mut dest_y,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
self.blit_region_unchecked(method, src, &src_region, dest_x, dest_y);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[rustfmt::skip]
|
||||||
|
pub unsafe fn blit_region_unchecked(
|
||||||
|
&mut self,
|
||||||
|
method: RgbaBlitMethod,
|
||||||
|
src: &Self,
|
||||||
|
src_region: &Rect,
|
||||||
|
dest_x: i32,
|
||||||
|
dest_y: i32,
|
||||||
|
) {
|
||||||
|
use RgbaBlitMethod::*;
|
||||||
|
match method {
|
||||||
|
Solid => self.solid_blit(src, src_region, dest_x, dest_y),
|
||||||
|
SolidFlipped { horizontal_flip, vertical_flip } => {
|
||||||
|
self.solid_flipped_blit(src, src_region, dest_x, dest_y, horizontal_flip, vertical_flip)
|
||||||
|
}
|
||||||
|
Transparent(transparent_color) => {
|
||||||
|
self.transparent_blit(src, src_region, dest_x, dest_y, transparent_color)
|
||||||
|
}
|
||||||
|
TransparentFlipped { transparent_color, horizontal_flip, vertical_flip } => {
|
||||||
|
self.transparent_flipped_blit(src, src_region, dest_x, dest_y, transparent_color, horizontal_flip, vertical_flip)
|
||||||
|
}
|
||||||
|
TransparentSingle { transparent_color, draw_color } => {
|
||||||
|
self.transparent_single_color_blit(src, src_region, dest_x, dest_y, transparent_color, draw_color)
|
||||||
|
}
|
||||||
|
TransparentFlippedSingle { transparent_color, horizontal_flip, vertical_flip, draw_color } => {
|
||||||
|
self.transparent_flipped_single_color_blit(src, src_region, dest_x, dest_y, transparent_color, horizontal_flip, vertical_flip, draw_color)
|
||||||
|
}
|
||||||
|
RotoZoom { angle, scale_x, scale_y } => {
|
||||||
|
self.rotozoom_blit(src, src_region, dest_x, dest_y, angle, scale_x, scale_y)
|
||||||
|
}
|
||||||
|
RotoZoomTransparent { angle, scale_x, scale_y, transparent_color } => {
|
||||||
|
self.rotozoom_transparent_blit(src, src_region, dest_x, dest_y, angle, scale_x, scale_y, transparent_color)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn blit(&mut self, method: RgbaBlitMethod, src: &Self, x: i32, y: i32) {
|
||||||
|
let src_region = Rect::new(0, 0, src.width, src.height);
|
||||||
|
self.blit_region(method, src, &src_region, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn blit_atlas(&mut self, method: RgbaBlitMethod, src: &BitmapAtlas<Self>, index: usize, x: i32, y: i32) {
|
||||||
|
if let Some(src_region) = src.get(index) {
|
||||||
|
self.blit_region(method, src.bitmap(), src_region, x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn blit_unchecked(&mut self, method: 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn blit_atlas_unchecked(&mut self, method: RgbaBlitMethod, src: &BitmapAtlas<Self>, index: usize, x: i32, y: i32) {
|
||||||
|
if let Some(src_region) = src.get(index) {
|
||||||
|
self.blit_region_unchecked(method, src.bitmap(), &src_region, x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9
ggdt/src/graphics/bitmap/rgb/mod.rs
Normal file
9
ggdt/src/graphics/bitmap/rgb/mod.rs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
use crate::graphics::bitmap::Bitmap;
|
||||||
|
|
||||||
|
pub mod blit;
|
||||||
|
|
||||||
|
pub type RgbaBitmap = Bitmap<u32>;
|
||||||
|
|
||||||
|
impl RgbaBitmap {
|
||||||
|
|
||||||
|
}
|
|
@ -2,7 +2,7 @@ use std::ops::Index;
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::graphics::bitmap::GeneralBitmap;
|
use crate::graphics::bitmap::general::GeneralBitmap;
|
||||||
use crate::math::rect::Rect;
|
use crate::math::rect::Rect;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
|
@ -134,13 +134,13 @@ where
|
||||||
pub mod tests {
|
pub mod tests {
|
||||||
use claim::*;
|
use claim::*;
|
||||||
|
|
||||||
use crate::graphics::indexed::bitmap::Bitmap;
|
use crate::graphics::bitmap::indexed::IndexedBitmap;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn adding_rects() {
|
pub fn adding_rects() {
|
||||||
let bmp = Bitmap::new(64, 64).unwrap();
|
let bmp = IndexedBitmap::new(64, 64).unwrap();
|
||||||
let mut atlas = BitmapAtlas::new(bmp);
|
let mut atlas = BitmapAtlas::new(bmp);
|
||||||
|
|
||||||
let rect = Rect::new(0, 0, 16, 16);
|
let rect = Rect::new(0, 0, 16, 16);
|
||||||
|
@ -174,7 +174,7 @@ pub mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn adding_grid() {
|
pub fn adding_grid() {
|
||||||
let bmp = Bitmap::new(64, 64).unwrap();
|
let bmp = IndexedBitmap::new(64, 64).unwrap();
|
||||||
let mut atlas = BitmapAtlas::new(bmp);
|
let mut atlas = BitmapAtlas::new(bmp);
|
||||||
|
|
||||||
assert_eq!(3, atlas.add_custom_grid(0, 0, 8, 8, 2, 2, 0).unwrap());
|
assert_eq!(3, atlas.add_custom_grid(0, 0, 8, 8, 2, 2, 0).unwrap());
|
||||||
|
|
|
@ -6,7 +6,7 @@ use byteorder::{ReadBytesExt, WriteBytesExt};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::graphics::color::{from_rgb32, luminance};
|
use crate::graphics::color::{from_rgb32, luminance};
|
||||||
use crate::graphics::indexed::palette::Palette;
|
use crate::graphics::palette::Palette;
|
||||||
use crate::math::lerp;
|
use crate::math::lerp;
|
||||||
use crate::utils::bytes::ReadFixedLengthByteArray;
|
use crate::utils::bytes::ReadFixedLengthByteArray;
|
||||||
|
|
|
@ -5,7 +5,7 @@ use std::path::Path;
|
||||||
use byteorder::{ReadBytesExt, WriteBytesExt};
|
use byteorder::{ReadBytesExt, WriteBytesExt};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::graphics::bitmap::GeneralBitmap;
|
use crate::graphics::bitmap::Bitmap;
|
||||||
use crate::graphics::Pixel;
|
use crate::graphics::Pixel;
|
||||||
use crate::math::rect::Rect;
|
use crate::math::rect::Rect;
|
||||||
|
|
||||||
|
@ -32,9 +32,9 @@ pub enum FontRenderOpts<PixelType: Pixel> {
|
||||||
|
|
||||||
pub trait Character {
|
pub trait Character {
|
||||||
fn bounds(&self) -> &Rect;
|
fn bounds(&self) -> &Rect;
|
||||||
fn draw<BitmapType>(&self, dest: &mut BitmapType, x: i32, y: i32, opts: FontRenderOpts<BitmapType::PixelType>)
|
fn draw<PixelType>(&self, dest: &mut Bitmap<PixelType>, x: i32, y: i32, opts: FontRenderOpts<PixelType>)
|
||||||
where
|
where
|
||||||
BitmapType: GeneralBitmap;
|
PixelType: Pixel;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Font {
|
pub trait Font {
|
||||||
|
@ -60,9 +60,9 @@ impl Character for BitmaskCharacter {
|
||||||
&self.bounds
|
&self.bounds
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw<BitmapType>(&self, dest: &mut BitmapType, x: i32, y: i32, opts: FontRenderOpts<BitmapType::PixelType>)
|
fn draw<PixelType>(&self, dest: &mut Bitmap<PixelType>, x: i32, y: i32, opts: FontRenderOpts<PixelType>)
|
||||||
where
|
where
|
||||||
BitmapType: GeneralBitmap
|
PixelType: Pixel
|
||||||
{
|
{
|
||||||
// out of bounds check
|
// out of bounds check
|
||||||
if ((x + self.bounds.width as i32) < dest.clip_region().x)
|
if ((x + self.bounds.width as i32) < dest.clip_region().x)
|
||||||
|
|
|
@ -1,617 +0,0 @@
|
||||||
use std::mem::swap;
|
|
||||||
|
|
||||||
use crate::graphics::font::{Character, Font, FontRenderOpts};
|
|
||||||
use crate::graphics::indexed::bitmap::Bitmap;
|
|
||||||
use crate::graphics::indexed::blendmap::BlendMap;
|
|
||||||
use crate::math::rect::Rect;
|
|
||||||
|
|
||||||
impl Bitmap {
|
|
||||||
/// Fills the entire bitmap with the given color.
|
|
||||||
pub fn clear(&mut self, color: u8) {
|
|
||||||
self.pixels.fill(color);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the pixel at the given coordinates to the color specified. If the coordinates lie
|
|
||||||
/// outside of the bitmaps clipping region, no pixels will be changed.
|
|
||||||
#[inline]
|
|
||||||
pub fn set_pixel(&mut self, x: i32, y: i32, color: u8) {
|
|
||||||
if let Some(pixels) = self.pixels_at_mut(x, y) {
|
|
||||||
pixels[0] = color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the pixel at the given coordinates using a blended color via the specified blend map,
|
|
||||||
/// or using the color specified if the blend map does not include the given color. If the
|
|
||||||
/// coordinates lie outside of the bitmaps clipping region, no pixels will be changed.
|
|
||||||
#[inline]
|
|
||||||
pub fn set_blended_pixel(&mut self, x: i32, y: i32, color: u8, blend_map: &BlendMap) {
|
|
||||||
if let Some(pixels) = self.pixels_at_mut(x, y) {
|
|
||||||
let dest_color = pixels[0];
|
|
||||||
if let Some(blended_color) = blend_map.blend(color, dest_color) {
|
|
||||||
pixels[0] = blended_color;
|
|
||||||
} else {
|
|
||||||
pixels[0] = color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the pixel at the given coordinates to the color specified. The coordinates are not
|
|
||||||
/// checked for validity, so it is up to you to ensure they lie within the bounds of the
|
|
||||||
/// bitmap.
|
|
||||||
#[inline]
|
|
||||||
pub unsafe fn set_pixel_unchecked(&mut self, x: i32, y: i32, color: u8) {
|
|
||||||
let p = self.pixels_at_mut_ptr_unchecked(x, y);
|
|
||||||
*p = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the pixel at the given coordinates using a blended color via the specified blend map,
|
|
||||||
/// or using the color specified if the blend map does not include the given color. The
|
|
||||||
/// coordinates are not checked for validity, so it is up to you to ensure they lie within the
|
|
||||||
/// bounds of the bitmap.
|
|
||||||
#[inline]
|
|
||||||
pub unsafe fn set_blended_pixel_unchecked(&mut self, x: i32, y: i32, color: u8, blend_map: &BlendMap) {
|
|
||||||
let p = self.pixels_at_mut_ptr_unchecked(x, y);
|
|
||||||
if let Some(blended_color) = blend_map.blend(color, *p) {
|
|
||||||
*p = blended_color;
|
|
||||||
} else {
|
|
||||||
*p = color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the pixel at the given coordinates. If the coordinates lie outside of the bitmaps
|
|
||||||
/// clipping region, None is returned.
|
|
||||||
#[inline]
|
|
||||||
pub fn get_pixel(&self, x: i32, y: i32) -> Option<u8> {
|
|
||||||
if let Some(pixels) = self.pixels_at(x, y) {
|
|
||||||
Some(pixels[0])
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the pixel at the given coordinates. The coordinates are not checked for validity, so
|
|
||||||
/// it is up to you to ensure they lie within the bounds of the bitmap.
|
|
||||||
#[inline]
|
|
||||||
pub unsafe fn get_pixel_unchecked(&self, x: i32, y: i32) -> u8 {
|
|
||||||
*(self.pixels_at_ptr_unchecked(x, y))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Renders a single character using the font given.
|
|
||||||
#[inline]
|
|
||||||
pub fn print_char<T: Font>(&mut self, ch: char, x: i32, y: i32, opts: FontRenderOpts<u8>, font: &T) {
|
|
||||||
font.character(ch)
|
|
||||||
.draw(self, x, y, opts);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Renders the string of text using the font given.
|
|
||||||
pub fn print_string<T: Font>(&mut self, text: &str, x: i32, y: i32, opts: FontRenderOpts<u8>, font: &T) {
|
|
||||||
let mut current_x = x;
|
|
||||||
let mut current_y = y;
|
|
||||||
for ch in text.chars() {
|
|
||||||
match ch {
|
|
||||||
' ' => current_x += font.space_width() as i32,
|
|
||||||
'\n' => {
|
|
||||||
current_x = x;
|
|
||||||
current_y += font.line_height() as i32
|
|
||||||
}
|
|
||||||
'\r' => (),
|
|
||||||
otherwise => {
|
|
||||||
self.print_char(otherwise, current_x, current_y, opts, font);
|
|
||||||
current_x += font.character(otherwise).bounds().width as i32;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Draws a line from x1,y1 to x2,y2.
|
|
||||||
pub fn line(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: u8) {
|
|
||||||
let mut dx = x1;
|
|
||||||
let mut dy = y1;
|
|
||||||
let delta_x = x2 - x1;
|
|
||||||
let delta_y = y2 - y1;
|
|
||||||
let delta_x_abs = delta_x.abs();
|
|
||||||
let delta_y_abs = delta_y.abs();
|
|
||||||
let delta_x_sign = delta_x.signum();
|
|
||||||
let delta_y_sign = delta_y.signum();
|
|
||||||
let mut x = delta_x_abs / 2;
|
|
||||||
let mut y = delta_y_abs / 2;
|
|
||||||
let offset_x_inc = delta_x_sign;
|
|
||||||
let offset_y_inc = delta_y_sign * self.width as i32;
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
// safety: while we are blindly getting a pointer to this x/y coordinate, we don't
|
|
||||||
// write to it unless we know the coordinates are in bounds.
|
|
||||||
// TODO: should be ok ... ? or am i making too many assumptions about memory layout?
|
|
||||||
let mut dest = self.pixels_at_mut_ptr_unchecked(x1, y1);
|
|
||||||
|
|
||||||
if self.is_xy_visible(dx, dy) {
|
|
||||||
*dest = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
if delta_x_abs >= delta_y_abs {
|
|
||||||
for _ in 0..delta_x_abs {
|
|
||||||
y += delta_y_abs;
|
|
||||||
|
|
||||||
if y >= delta_x_abs {
|
|
||||||
y -= delta_x_abs;
|
|
||||||
dy += delta_y_sign;
|
|
||||||
dest = dest.offset(offset_y_inc as isize);
|
|
||||||
}
|
|
||||||
|
|
||||||
dx += delta_x_sign;
|
|
||||||
dest = dest.offset(offset_x_inc as isize);
|
|
||||||
|
|
||||||
if self.is_xy_visible(dx, dy) {
|
|
||||||
*dest = color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for _ in 0..delta_y_abs {
|
|
||||||
x += delta_x_abs;
|
|
||||||
|
|
||||||
if x >= delta_y_abs {
|
|
||||||
x -= delta_y_abs;
|
|
||||||
dx += delta_x_sign;
|
|
||||||
dest = dest.offset(offset_x_inc as isize);
|
|
||||||
}
|
|
||||||
|
|
||||||
dy += delta_y_sign;
|
|
||||||
dest = dest.offset(offset_y_inc as isize);
|
|
||||||
|
|
||||||
if self.is_xy_visible(dx, dy) {
|
|
||||||
*dest = color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Draws a line from x1,y1 to x2,y2 by blending the drawn pixels using the given blend map,
|
|
||||||
/// or the color specified if the blend map does not include this color.
|
|
||||||
pub fn blended_line(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: u8, blend_map: &BlendMap) {
|
|
||||||
if let Some(blend_mapping) = blend_map.get_mapping(color) {
|
|
||||||
let mut dx = x1;
|
|
||||||
let mut dy = y1;
|
|
||||||
let delta_x = x2 - x1;
|
|
||||||
let delta_y = y2 - y1;
|
|
||||||
let delta_x_abs = delta_x.abs();
|
|
||||||
let delta_y_abs = delta_y.abs();
|
|
||||||
let delta_x_sign = delta_x.signum();
|
|
||||||
let delta_y_sign = delta_y.signum();
|
|
||||||
let mut x = delta_x_abs / 2;
|
|
||||||
let mut y = delta_y_abs / 2;
|
|
||||||
let offset_x_inc = delta_x_sign;
|
|
||||||
let offset_y_inc = delta_y_sign * self.width as i32;
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
// safety: while we are blindly getting a pointer to this x/y coordinate, we don't
|
|
||||||
// write to it unless we know the coordinates are in bounds.
|
|
||||||
// TODO: should be ok ... ? or am i making too many assumptions about memory layout?
|
|
||||||
let mut dest = self.pixels_at_mut_ptr_unchecked(x1, y1);
|
|
||||||
|
|
||||||
if self.is_xy_visible(dx, dy) {
|
|
||||||
*dest = blend_mapping[*dest as usize];
|
|
||||||
}
|
|
||||||
|
|
||||||
if delta_x_abs >= delta_y_abs {
|
|
||||||
for _ in 0..delta_x_abs {
|
|
||||||
y += delta_y_abs;
|
|
||||||
|
|
||||||
if y >= delta_x_abs {
|
|
||||||
y -= delta_x_abs;
|
|
||||||
dy += delta_y_sign;
|
|
||||||
dest = dest.offset(offset_y_inc as isize);
|
|
||||||
}
|
|
||||||
|
|
||||||
dx += delta_x_sign;
|
|
||||||
dest = dest.offset(offset_x_inc as isize);
|
|
||||||
|
|
||||||
if self.is_xy_visible(dx, dy) {
|
|
||||||
*dest = blend_mapping[*dest as usize];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for _ in 0..delta_y_abs {
|
|
||||||
x += delta_x_abs;
|
|
||||||
|
|
||||||
if x >= delta_y_abs {
|
|
||||||
x -= delta_y_abs;
|
|
||||||
dx += delta_x_sign;
|
|
||||||
dest = dest.offset(offset_x_inc as isize);
|
|
||||||
}
|
|
||||||
|
|
||||||
dy += delta_y_sign;
|
|
||||||
dest = dest.offset(offset_y_inc as isize);
|
|
||||||
|
|
||||||
if self.is_xy_visible(dx, dy) {
|
|
||||||
*dest = blend_mapping[*dest as usize];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.line(x1, y1, x2, y2, color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Draws a horizontal line from x1,y to x2,y.
|
|
||||||
pub fn horiz_line(&mut self, x1: i32, x2: i32, y: i32, color: u8) {
|
|
||||||
let mut region = Rect::from_coords(x1, y, x2, y);
|
|
||||||
if region.clamp_to(&self.clip_region) {
|
|
||||||
unsafe {
|
|
||||||
let dest = self.pixels_at_mut_ptr_unchecked(region.x, region.y);
|
|
||||||
dest.write_bytes(color, region.width as usize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Draws a horizontal line from x1,y to x2,y by blending the drawn pixels using the given
|
|
||||||
/// blend map, or the color specified if the blend map does not include this color.
|
|
||||||
pub fn blended_horiz_line(&mut self, x1: i32, x2: i32, y: i32, color: u8, blend_map: &BlendMap) {
|
|
||||||
if let Some(blend_mapping) = blend_map.get_mapping(color) {
|
|
||||||
let mut region = Rect::from_coords(x1, y, x2, y);
|
|
||||||
if region.clamp_to(&self.clip_region) {
|
|
||||||
unsafe {
|
|
||||||
let dest = self.pixels_at_mut_unchecked(region.x, region.y);
|
|
||||||
for x in 0..region.width as usize {
|
|
||||||
dest[x] = blend_mapping[dest[x] as usize];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.horiz_line(x1, x2, y, color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Draws a vertical line from x,y1 to x,y2.
|
|
||||||
pub fn vert_line(&mut self, x: i32, y1: i32, y2: i32, color: u8) {
|
|
||||||
let mut region = Rect::from_coords(x, y1, x, y2);
|
|
||||||
if region.clamp_to(&self.clip_region) {
|
|
||||||
unsafe {
|
|
||||||
let mut dest = self.pixels_at_mut_ptr_unchecked(region.x, region.y);
|
|
||||||
for _ in 0..region.height {
|
|
||||||
*dest = color;
|
|
||||||
dest = dest.add(self.width as usize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Draws a vertical line from x,y1 to x,y2 by blending the drawn pixels using the given blend
|
|
||||||
/// map, or the color specified if the blend map does not include this color.
|
|
||||||
pub fn blended_vert_line(&mut self, x: i32, y1: i32, y2: i32, color: u8, blend_map: &BlendMap) {
|
|
||||||
if let Some(blend_mapping) = blend_map.get_mapping(color) {
|
|
||||||
let mut region = Rect::from_coords(x, y1, x, y2);
|
|
||||||
if region.clamp_to(&self.clip_region) {
|
|
||||||
unsafe {
|
|
||||||
let mut dest = self.pixels_at_mut_ptr_unchecked(region.x, region.y);
|
|
||||||
for _ in 0..region.height {
|
|
||||||
*dest = blend_mapping[*dest as usize];
|
|
||||||
dest = dest.add(self.width as usize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.vert_line(x, y1, y2, color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Draws an empty box (rectangle) using the points x1,y1 and x2,y2 to form the box to be
|
|
||||||
/// drawn, assuming they are specifying the top-left and bottom-right corners respectively.
|
|
||||||
pub fn rect(&mut self, mut x1: i32, mut y1: i32, mut x2: i32, mut y2: i32, color: u8) {
|
|
||||||
// note: need to manually do all this instead of just relying on Rect::from_coords (which
|
|
||||||
// could otherwise figure all this out for us) mainly just because we need the post-swap
|
|
||||||
// x1,y1,x2,y2 values for post-region-clamping comparison purposes ...
|
|
||||||
if x2 < x1 {
|
|
||||||
swap(&mut x1, &mut x2);
|
|
||||||
}
|
|
||||||
if y2 < y1 {
|
|
||||||
swap(&mut y1, &mut y2);
|
|
||||||
}
|
|
||||||
let mut region = Rect {
|
|
||||||
x: x1,
|
|
||||||
y: y1,
|
|
||||||
width: (x2 - x1 + 1) as u32,
|
|
||||||
height: (y2 - y1 + 1) as u32,
|
|
||||||
};
|
|
||||||
if !region.clamp_to(&self.clip_region) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// top line, only if y1 was originally within bounds
|
|
||||||
if y1 == region.y {
|
|
||||||
unsafe {
|
|
||||||
let dest = self.pixels_at_mut_ptr_unchecked(region.x, region.y);
|
|
||||||
dest.write_bytes(color, region.width as usize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// bottom line, only if y2 was originally within bounds
|
|
||||||
if y2 == region.bottom() {
|
|
||||||
unsafe {
|
|
||||||
let dest = self.pixels_at_mut_ptr_unchecked(region.x, region.bottom());
|
|
||||||
dest.write_bytes(color, region.width as usize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// left line, only if x1 was originally within bounds
|
|
||||||
if x1 == region.x {
|
|
||||||
unsafe {
|
|
||||||
let mut dest = self.pixels_at_mut_ptr_unchecked(region.x, region.y);
|
|
||||||
for _ in 0..region.height {
|
|
||||||
*dest = color;
|
|
||||||
dest = dest.add(self.width as usize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// right line, only if x2 was originally within bounds
|
|
||||||
if x2 == region.right() {
|
|
||||||
unsafe {
|
|
||||||
let mut dest = self.pixels_at_mut_ptr_unchecked(region.right(), region.y);
|
|
||||||
for _ in 0..region.height {
|
|
||||||
*dest = color;
|
|
||||||
dest = dest.add(self.width as usize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Draws an empty box (rectangle) using the points x1,y1 and x2,y2 to form the box to be
|
|
||||||
/// drawn, assuming they are specifying the top-left and bottom-right corners respectively.
|
|
||||||
/// The box is drawn by blending the drawn pixels using the given blend map, or the color
|
|
||||||
/// specified if the blend map does not include this color.
|
|
||||||
pub fn blended_rect(&mut self, mut x1: i32, mut y1: i32, mut x2: i32, mut y2: i32, color: u8, blend_map: &BlendMap) {
|
|
||||||
if let Some(blend_mapping) = blend_map.get_mapping(color) {
|
|
||||||
// note: need to manually do all this instead of just relying on Rect::from_coords (which
|
|
||||||
// could otherwise figure all this out for us) mainly just because we need the post-swap
|
|
||||||
// x1,y1,x2,y2 values for post-region-clamping comparison purposes ...
|
|
||||||
if x2 < x1 {
|
|
||||||
swap(&mut x1, &mut x2);
|
|
||||||
}
|
|
||||||
if y2 < y1 {
|
|
||||||
swap(&mut y1, &mut y2);
|
|
||||||
}
|
|
||||||
let mut region = Rect {
|
|
||||||
x: x1,
|
|
||||||
y: y1,
|
|
||||||
width: (x2 - x1 + 1) as u32,
|
|
||||||
height: (y2 - y1 + 1) as u32,
|
|
||||||
};
|
|
||||||
if !region.clamp_to(&self.clip_region) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// note that since we're performing a blend based on the existing destination pixel,
|
|
||||||
// we need to make sure that we don't draw any overlapping corner pixels (where we
|
|
||||||
// would end up blending with the edge of a previously drawn line).
|
|
||||||
// to solve this issue, we just cut off the left-most and right-most pixels for the
|
|
||||||
// two horizontal lines drawn. those corner pixels will be drawn during the vertical
|
|
||||||
// line drawing instead.
|
|
||||||
|
|
||||||
// top line, only if y1 was originally within bounds
|
|
||||||
if y1 == region.y {
|
|
||||||
unsafe {
|
|
||||||
let dest = self.pixels_at_mut_unchecked(region.x, region.y);
|
|
||||||
for x in 1..(region.width - 1) as usize {
|
|
||||||
dest[x] = blend_mapping[dest[x] as usize];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// bottom line, only if y2 was originally within bounds
|
|
||||||
if y2 == region.bottom() {
|
|
||||||
unsafe {
|
|
||||||
let dest = self.pixels_at_mut_unchecked(region.x, region.bottom());
|
|
||||||
for x in 1..(region.width - 1) as usize {
|
|
||||||
dest[x] = blend_mapping[dest[x] as usize];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// left line, only if x1 was originally within bounds
|
|
||||||
if x1 == region.x {
|
|
||||||
unsafe {
|
|
||||||
let mut dest = self.pixels_at_mut_ptr_unchecked(region.x, region.y);
|
|
||||||
for _ in 0..region.height {
|
|
||||||
*dest = blend_mapping[*dest as usize];
|
|
||||||
dest = dest.add(self.width as usize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// right line, only if x2 was originally within bounds
|
|
||||||
if x2 == region.right() {
|
|
||||||
unsafe {
|
|
||||||
let mut dest = self.pixels_at_mut_ptr_unchecked(region.right(), region.y);
|
|
||||||
for _ in 0..region.height {
|
|
||||||
*dest = blend_mapping[*dest as usize];
|
|
||||||
dest = dest.add(self.width as usize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.rect(x1, y1, x2, y2, color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Draws a filled box (rectangle) using the points x1,y1 and x2,y2 to form the box to be
|
|
||||||
/// drawn, assuming they are specifying the top-left and bottom-right corners respectively.
|
|
||||||
pub fn filled_rect(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: u8) {
|
|
||||||
let mut region = Rect::from_coords(x1, y1, x2, y2);
|
|
||||||
if region.clamp_to(&self.clip_region) {
|
|
||||||
unsafe {
|
|
||||||
let mut dest = self.pixels_at_mut_ptr_unchecked(region.x, region.y);
|
|
||||||
for _ in 0..region.height {
|
|
||||||
dest.write_bytes(color, region.width as usize);
|
|
||||||
dest = dest.add(self.width as usize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Draws a filled box (rectangle) using the points x1,y1 and x2,y2 to form the box to be
|
|
||||||
/// drawn, assuming they are specifying the top-left and bottom-right corners respectively. The
|
|
||||||
/// filled box is draw by blending the drawn pixels using the given blend map, or the color
|
|
||||||
/// specified if the blend map does not include this color.
|
|
||||||
pub fn blended_filled_rect(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: u8, blend_map: &BlendMap) {
|
|
||||||
if let Some(blend_mapping) = blend_map.get_mapping(color) {
|
|
||||||
let mut region = Rect::from_coords(x1, y1, x2, y2);
|
|
||||||
if region.clamp_to(&self.clip_region) {
|
|
||||||
unsafe {
|
|
||||||
let mut dest = self.pixels_at_mut_ptr_unchecked(region.x, region.y);
|
|
||||||
for _ in 0..region.height {
|
|
||||||
for x in 0..region.width as usize {
|
|
||||||
let dest_x = dest.offset(x as isize);
|
|
||||||
*dest_x = blend_mapping[*dest_x as usize];
|
|
||||||
}
|
|
||||||
dest = dest.add(self.width as usize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.filled_rect(x1, y1, x2, y2, color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Draws the outline of a circle formed by the center point and radius given.
|
|
||||||
pub fn circle(&mut self, center_x: i32, center_y: i32, radius: u32, color: u8) {
|
|
||||||
// TODO: optimize
|
|
||||||
let mut x = 0;
|
|
||||||
let mut y = radius as i32;
|
|
||||||
let mut m = 5 - 4 * radius as i32;
|
|
||||||
|
|
||||||
while x <= y {
|
|
||||||
self.set_pixel(center_x + x, center_y + y, color);
|
|
||||||
self.set_pixel(center_x + x, center_y - y, color);
|
|
||||||
self.set_pixel(center_x - x, center_y + y, color);
|
|
||||||
self.set_pixel(center_x - x, center_y - y, color);
|
|
||||||
self.set_pixel(center_x + y, center_y + x, color);
|
|
||||||
self.set_pixel(center_x + y, center_y - x, color);
|
|
||||||
self.set_pixel(center_x - y, center_y + x, color);
|
|
||||||
self.set_pixel(center_x - y, center_y - x, color);
|
|
||||||
|
|
||||||
if m > 0 {
|
|
||||||
y -= 1;
|
|
||||||
m -= 8 * y;
|
|
||||||
}
|
|
||||||
|
|
||||||
x += 1;
|
|
||||||
m += 8 * x + 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Draws a filled circle formed by the center point and radius given.
|
|
||||||
pub fn filled_circle(&mut self, center_x: i32, center_y: i32, radius: u32, color: u8) {
|
|
||||||
// TODO: optimize
|
|
||||||
let mut x = 0;
|
|
||||||
let mut y = radius as i32;
|
|
||||||
let mut m = 5 - 4 * radius as i32;
|
|
||||||
|
|
||||||
while x <= y {
|
|
||||||
self.horiz_line(center_x - x, center_x + x, center_y - y, color);
|
|
||||||
self.horiz_line(center_x - y, center_x + y, center_y - x, color);
|
|
||||||
self.horiz_line(center_x - y, center_x + y, center_y + x, color);
|
|
||||||
self.horiz_line(center_x - x, center_x + x, center_y + y, color);
|
|
||||||
|
|
||||||
if m > 0 {
|
|
||||||
y -= 1;
|
|
||||||
m -= 8 * y;
|
|
||||||
}
|
|
||||||
|
|
||||||
x += 1;
|
|
||||||
m += 8 * x + 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
pub mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[rustfmt::skip]
|
|
||||||
#[test]
|
|
||||||
pub fn set_and_get_pixel() {
|
|
||||||
let mut bmp = Bitmap::new(8, 8).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(None, bmp.get_pixel(-1, -1));
|
|
||||||
|
|
||||||
assert_eq!(0, bmp.get_pixel(0, 0).unwrap());
|
|
||||||
bmp.set_pixel(0, 0, 7);
|
|
||||||
assert_eq!(7, bmp.get_pixel(0, 0).unwrap());
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
bmp.pixels(),
|
|
||||||
&[
|
|
||||||
7, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(0, bmp.get_pixel(2, 4).unwrap());
|
|
||||||
bmp.set_pixel(2, 4, 5);
|
|
||||||
assert_eq!(5, bmp.get_pixel(2, 4).unwrap());
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
bmp.pixels(),
|
|
||||||
&[
|
|
||||||
7, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 5, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rustfmt::skip]
|
|
||||||
#[test]
|
|
||||||
pub fn set_and_get_pixel_unchecked() {
|
|
||||||
let mut bmp = Bitmap::new(8, 8).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(0, unsafe { bmp.get_pixel_unchecked(0, 0) });
|
|
||||||
unsafe { bmp.set_pixel_unchecked(0, 0, 7) };
|
|
||||||
assert_eq!(7, unsafe { bmp.get_pixel_unchecked(0, 0) });
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
bmp.pixels(),
|
|
||||||
&[
|
|
||||||
7, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(0, unsafe { bmp.get_pixel_unchecked(2, 4) });
|
|
||||||
unsafe { bmp.set_pixel_unchecked(2, 4, 5) };
|
|
||||||
assert_eq!(5, unsafe { bmp.get_pixel_unchecked(2, 4) });
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
bmp.pixels(),
|
|
||||||
&[
|
|
||||||
7, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 5, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
//! This module and all sub-modules contain graphics functionality that uses indexed colours. That is, each pixel
|
|
||||||
//! is a `u8` and treated as index into a [`Palette`], so 256 maximum colours are possible.
|
|
||||||
|
|
||||||
pub mod bitmap;
|
|
||||||
pub mod blendmap;
|
|
||||||
pub mod palette;
|
|
||||||
|
|
||||||
pub mod prelude;
|
|
|
@ -1,22 +0,0 @@
|
||||||
// include all things useable for indexed colour graphics
|
|
||||||
|
|
||||||
pub use crate::graphics::{
|
|
||||||
bitmap::*,
|
|
||||||
bitmapatlas::*,
|
|
||||||
color::*,
|
|
||||||
font::*,
|
|
||||||
indexed::{
|
|
||||||
*,
|
|
||||||
bitmap::{
|
|
||||||
*,
|
|
||||||
blit::*,
|
|
||||||
gif::*,
|
|
||||||
iff::*,
|
|
||||||
pcx::*,
|
|
||||||
primitives::*,
|
|
||||||
},
|
|
||||||
blendmap::*,
|
|
||||||
palette::*,
|
|
||||||
},
|
|
||||||
Pixel,
|
|
||||||
};
|
|
|
@ -4,11 +4,11 @@ pub mod bitmap;
|
||||||
pub mod bitmapatlas;
|
pub mod bitmapatlas;
|
||||||
pub mod color;
|
pub mod color;
|
||||||
pub mod font;
|
pub mod font;
|
||||||
pub mod indexed;
|
pub mod blendmap;
|
||||||
pub mod rgb;
|
pub mod palette;
|
||||||
|
|
||||||
pub mod prelude;
|
pub mod prelude;
|
||||||
|
|
||||||
/// Common trait to represent single pixel/colour values.
|
/// Common trait to represent single pixel/colour values.
|
||||||
pub trait Pixel: PrimInt + Unsigned {}
|
pub trait Pixel: PrimInt + Unsigned + Default {}
|
||||||
impl<T> Pixel for T where T: PrimInt + Unsigned {}
|
impl<T> Pixel for T where T: PrimInt + Unsigned + Default {}
|
||||||
|
|
|
@ -7,8 +7,8 @@ use std::path::Path;
|
||||||
use byteorder::{ReadBytesExt, WriteBytesExt};
|
use byteorder::{ReadBytesExt, WriteBytesExt};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use crate::graphics::bitmap::indexed::IndexedBitmap;
|
||||||
use crate::graphics::color::{from_rgb32, lerp_rgb32, to_rgb32};
|
use crate::graphics::color::{from_rgb32, lerp_rgb32, to_rgb32};
|
||||||
use crate::graphics::indexed::bitmap::Bitmap;
|
|
||||||
use crate::NUM_COLORS;
|
use crate::NUM_COLORS;
|
||||||
use crate::utils::abs_diff;
|
use crate::utils::abs_diff;
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ use crate::utils::abs_diff;
|
||||||
pub trait ColorRange: RangeBounds<u8> + Iterator<Item=u8> {}
|
pub trait ColorRange: RangeBounds<u8> + Iterator<Item=u8> {}
|
||||||
impl<T> ColorRange for T where T: RangeBounds<u8> + Iterator<Item=u8> {}
|
impl<T> ColorRange for T where T: RangeBounds<u8> + Iterator<Item=u8> {}
|
||||||
|
|
||||||
pub static VGA_PALETTE_BYTES: &[u8] = include_bytes!("../../../assets/vga.pal");
|
pub static VGA_PALETTE_BYTES: &[u8] = include_bytes!("../../assets/vga.pal");
|
||||||
|
|
||||||
// vga bios (0-63) format
|
// vga bios (0-63) format
|
||||||
fn read_palette_6bit<T: ReadBytesExt>(
|
fn read_palette_6bit<T: ReadBytesExt>(
|
||||||
|
@ -484,7 +484,7 @@ impl Palette {
|
||||||
/// pixel is one of the colors from this palette, in ascending order, left-to-right,
|
/// pixel is one of the colors from this palette, in ascending order, left-to-right,
|
||||||
/// top-to-bottom. The coordinates given specify the top-left coordinate on the destination
|
/// top-to-bottom. The coordinates given specify the top-left coordinate on the destination
|
||||||
/// bitmap to begin drawing the palette at.
|
/// bitmap to begin drawing the palette at.
|
||||||
pub fn draw(&self, dest: &mut Bitmap, x: i32, y: i32) {
|
pub fn draw(&self, dest: &mut IndexedBitmap, x: i32, y: i32) {
|
||||||
let mut color = 0;
|
let mut color = 0;
|
||||||
for yd in 0..16 {
|
for yd in 0..16 {
|
||||||
for xd in 0..16 {
|
for xd in 0..16 {
|
|
@ -1,9 +1,27 @@
|
||||||
pub use crate::graphics::{
|
pub use crate::graphics::{
|
||||||
*,
|
*,
|
||||||
bitmap::*,
|
bitmap::{
|
||||||
|
*,
|
||||||
|
blit::*,
|
||||||
|
general::*,
|
||||||
|
gif::*,
|
||||||
|
iff::*,
|
||||||
|
indexed::{
|
||||||
|
*,
|
||||||
|
blit::*,
|
||||||
|
primitives::*,
|
||||||
|
},
|
||||||
|
pcx::*,
|
||||||
|
primitives::*,
|
||||||
|
rgb::{
|
||||||
|
*,
|
||||||
|
blit::*,
|
||||||
|
},
|
||||||
|
},
|
||||||
bitmapatlas::*,
|
bitmapatlas::*,
|
||||||
|
blendmap::*,
|
||||||
color::*,
|
color::*,
|
||||||
font::*,
|
font::*,
|
||||||
indexed::prelude::*,
|
palette::*,
|
||||||
rgb::prelude::*,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
//! This module and all sub-modules contain graphics functionality that uses 32-bit colour data. That is, each pixel
|
|
||||||
//! is a `u32` and contains full RGBA information.
|
|
||||||
|
|
||||||
pub mod prelude;
|
|
|
@ -1,14 +0,0 @@
|
||||||
// include all things useable for 32-bit colour graphics
|
|
||||||
|
|
||||||
pub use crate::graphics::{
|
|
||||||
bitmap::*,
|
|
||||||
bitmapatlas::*,
|
|
||||||
color::*,
|
|
||||||
font::*,
|
|
||||||
Pixel,
|
|
||||||
rgb::{
|
|
||||||
*,
|
|
||||||
// todo
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
// to get everything this library has to offer, including all `SystemResources` implementations
|
|
||||||
|
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
*,
|
*,
|
||||||
audio::prelude::*,
|
audio::prelude::*,
|
||||||
|
@ -17,26 +15,3 @@ pub use crate::{
|
||||||
},
|
},
|
||||||
utils::prelude::*,
|
utils::prelude::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
// specific module preludes that can be used instead that grab everything relevant to a specific `SystemResources`
|
|
||||||
// implementation only, since most applications will only use one and not care about the rest
|
|
||||||
|
|
||||||
pub mod dos_like {
|
|
||||||
pub use crate::{
|
|
||||||
*,
|
|
||||||
audio::prelude::*,
|
|
||||||
base::*,
|
|
||||||
entities::*,
|
|
||||||
events::*,
|
|
||||||
graphics::indexed::prelude::*,
|
|
||||||
math::prelude::*,
|
|
||||||
states::*,
|
|
||||||
system::{
|
|
||||||
prelude::*,
|
|
||||||
res::{
|
|
||||||
dos_like::*,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
utils::prelude::*,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::graphics::bitmap::{GeneralBitmap, GeneralBlitMethod};
|
use crate::graphics::bitmap::general::{GeneralBitmap, GeneralBlitMethod};
|
||||||
use crate::graphics::indexed;
|
use crate::graphics::bitmap::indexed::IndexedBitmap;
|
||||||
use crate::math::rect::Rect;
|
use crate::math::rect::Rect;
|
||||||
use crate::system::input_devices::mouse::Mouse;
|
use crate::system::input_devices::mouse::Mouse;
|
||||||
|
|
||||||
|
@ -205,8 +205,8 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DefaultMouseCursorBitmaps<indexed::bitmap::Bitmap> for CustomMouseCursor<indexed::bitmap::Bitmap> {
|
impl DefaultMouseCursorBitmaps<IndexedBitmap> for CustomMouseCursor<IndexedBitmap> {
|
||||||
fn get_default() -> MouseCursorBitmap<indexed::bitmap::Bitmap> {
|
fn get_default() -> MouseCursorBitmap<IndexedBitmap> {
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
const CURSOR_PIXELS: [u8; DEFAULT_MOUSE_CURSOR_WIDTH * DEFAULT_MOUSE_CURSOR_HEIGHT] = [
|
const CURSOR_PIXELS: [u8; DEFAULT_MOUSE_CURSOR_WIDTH * DEFAULT_MOUSE_CURSOR_HEIGHT] = [
|
||||||
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
@ -227,7 +227,7 @@ impl DefaultMouseCursorBitmaps<indexed::bitmap::Bitmap> for CustomMouseCursor<in
|
||||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut cursor = indexed::bitmap::Bitmap::new(
|
let mut cursor = IndexedBitmap::new(
|
||||||
DEFAULT_MOUSE_CURSOR_WIDTH as u32,
|
DEFAULT_MOUSE_CURSOR_WIDTH as u32,
|
||||||
DEFAULT_MOUSE_CURSOR_HEIGHT as u32,
|
DEFAULT_MOUSE_CURSOR_HEIGHT as u32,
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
|
@ -32,8 +32,8 @@ use crate::{DEFAULT_SCALE_FACTOR, SCREEN_HEIGHT, SCREEN_WIDTH};
|
||||||
use crate::audio::{Audio, TARGET_AUDIO_CHANNELS, TARGET_AUDIO_FREQUENCY};
|
use crate::audio::{Audio, TARGET_AUDIO_CHANNELS, TARGET_AUDIO_FREQUENCY};
|
||||||
use crate::audio::queue::AudioQueue;
|
use crate::audio::queue::AudioQueue;
|
||||||
use crate::graphics::font::BitmaskFont;
|
use crate::graphics::font::BitmaskFont;
|
||||||
use crate::graphics::indexed::bitmap::Bitmap;
|
use crate::graphics::bitmap::indexed::IndexedBitmap;
|
||||||
use crate::graphics::indexed::palette::Palette;
|
use crate::graphics::palette::Palette;
|
||||||
use crate::system::event::{SystemEvent, SystemEventHandler};
|
use crate::system::event::{SystemEvent, SystemEventHandler};
|
||||||
use crate::system::input_devices::InputDevice;
|
use crate::system::input_devices::InputDevice;
|
||||||
use crate::system::input_devices::keyboard::Keyboard;
|
use crate::system::input_devices::keyboard::Keyboard;
|
||||||
|
@ -140,7 +140,7 @@ impl SystemResourcesConfig for DosLikeConfig {
|
||||||
// create the Bitmap object that will be exposed to the application acting as the system
|
// create the Bitmap object that will be exposed to the application acting as the system
|
||||||
// backbuffer
|
// backbuffer
|
||||||
|
|
||||||
let framebuffer = match Bitmap::new(SCREEN_WIDTH, SCREEN_HEIGHT) {
|
let framebuffer = match IndexedBitmap::new(SCREEN_WIDTH, SCREEN_HEIGHT) {
|
||||||
Ok(bmp) => bmp,
|
Ok(bmp) => bmp,
|
||||||
Err(error) => return Err(SystemResourcesError::SDLError(error.to_string())),
|
Err(error) => return Err(SystemResourcesError::SDLError(error.to_string())),
|
||||||
};
|
};
|
||||||
|
@ -218,7 +218,7 @@ pub struct DosLike {
|
||||||
/// The primary backbuffer [`Bitmap`] that will be rendered to the screen whenever
|
/// The primary backbuffer [`Bitmap`] that will be rendered to the screen whenever
|
||||||
/// [`System::display`] is called. Regardless of the actual window size, this bitmap is always
|
/// [`System::display`] is called. Regardless of the actual window size, this bitmap is always
|
||||||
/// [`SCREEN_WIDTH`]x[`SCREEN_HEIGHT`] pixels in size.
|
/// [`SCREEN_WIDTH`]x[`SCREEN_HEIGHT`] pixels in size.
|
||||||
pub video: Bitmap,
|
pub video: IndexedBitmap,
|
||||||
|
|
||||||
/// A pre-loaded [`Font`] that can be used for text rendering.
|
/// A pre-loaded [`Font`] that can be used for text rendering.
|
||||||
pub font: BitmaskFont,
|
pub font: BitmaskFont,
|
||||||
|
@ -233,7 +233,7 @@ pub struct DosLike {
|
||||||
|
|
||||||
/// Manages custom mouse cursor graphics and state. Use this to set/unset a custom mouse cursor bitmap.
|
/// Manages custom mouse cursor graphics and state. Use this to set/unset a custom mouse cursor bitmap.
|
||||||
/// When set, rendering should occur automatically during calls to [`SystemResources::display`].
|
/// When set, rendering should occur automatically during calls to [`SystemResources::display`].
|
||||||
pub cursor: CustomMouseCursor<Bitmap>,
|
pub cursor: CustomMouseCursor<IndexedBitmap>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for DosLike {
|
impl std::fmt::Debug for DosLike {
|
||||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue