# HG changeset patch # User alfadur # Date 1541650522 -10800 # Node ID a4c1a2d0ac246044d89f16b10aa7d89d5c7e2f65 # Parent e2c51c8e0b2e403eb5c84eeb736238aa18fa3a57 implement basic land bordering diff -r e2c51c8e0b2e -r a4c1a2d0ac24 rust/land2d/src/lib.rs --- a/rust/land2d/src/lib.rs Thu Nov 08 00:00:58 2018 +0100 +++ b/rust/land2d/src/lib.rs Thu Nov 08 07:15:22 2018 +0300 @@ -84,7 +84,7 @@ } #[inline] - pub fn rows(&self) -> impl Iterator { + pub fn rows(&self) -> impl DoubleEndedIterator { self.pixels.rows() } diff -r e2c51c8e0b2e -r a4c1a2d0ac24 rust/mapgen/src/lib.rs --- a/rust/mapgen/src/lib.rs Thu Nov 08 00:00:58 2018 +0100 +++ b/rust/mapgen/src/lib.rs Thu Nov 08 07:15:22 2018 +0300 @@ -108,6 +108,7 @@ pub fn make_texture(&self, land: &Land2D, theme: &Theme) -> Vec2D { let mut texture = Vec2D::new(land.size(), 0); + if let Some(land_sprite) = theme.land_texture() { for (row_index, (land_row, tex_row)) in land.rows() .zip(texture.rows_mut()) @@ -136,10 +137,106 @@ } } } + + if let Some(border_sprite) = theme.border_texture() { + assert!(border_sprite.height() <= 512); + let border_width = (border_sprite.height() / 2) as u8; + + let mut offsets = vec![255u8; land.width()]; + + land_border_pass( + land.rows().rev().zip(texture.rows_mut().rev()), + &mut offsets, + border_width, + |x, y| border_sprite.get_pixel( + x % border_sprite.width(), + border_sprite.height() - 1 - y, + ) + ); + + offsets.iter_mut().for_each(|v| *v = 255); + + land_border_pass( + land.rows().zip(texture.rows_mut()), + &mut offsets, + border_width, + |x, y| border_sprite.get_pixel( + x % border_sprite.width(), + y, + ) + ); + } + texture } } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +struct Color(u32); + +impl Color { + #[inline] + fn red(self) -> u8 { + (self.0 >> 0 & 0xFF) as u8 + } + + #[inline] + fn green(self) -> u8 { + (self.0 >> 8 & 0xFF) as u8 + } + + #[inline] + fn blue(self) -> u8 { + (self.0 >> 16 & 0xFF) as u8 + } + + #[inline] + fn alpha(self) -> u8 { + (self.0 >> 24 & 0xFF) as u8 + } +} + +#[inline] +fn lerp(from: u8, to: u8, coef: u8) -> u8 { + ((from as u16 * (256 - coef as u16) + to as u16 * coef as u16) / 256) as u8 +} + +#[inline] +fn blend(source: u32, target: u32) -> u32 { + let source = Color(source); + let target = Color(target); + let alpha = lerp(target.alpha(), 255, source.alpha()); + let red = lerp(target.red(), source.red(), source.alpha()); + let green = lerp(target.green(), source.green(), source.alpha()); + let blue = lerp(target.blue(), source.blue(), source.alpha()); + (red as u32) << 0 | (green as u32) << 8 | (blue as u32) << 16 | (alpha as u32) << 24 +} + +fn land_border_pass<'a, T, F>(rows: T, offsets: &mut [u8], border_width: u8, pixel_getter: F) + where T: Iterator, + F: (Fn(usize, usize) -> u32) +{ + for (land_row, tex_row) in rows { + for (x, ((land_v, tex_v), offset_v)) in land_row.iter() + .zip(tex_row.iter_mut()) + .zip(offsets.iter_mut()) + .enumerate() + { + *offset_v = if *land_v == 0 { + if *offset_v < border_width { + *tex_v = blend( + pixel_getter(x, *offset_v as usize), + *tex_v, + ) + } + offset_v.saturating_add(1) + } else { + 0 + } + } + } +} + fn tex_row_copy(land_row: &[u8], tex_row: &mut [u32], sprite_row: &[u32]) { for ((land_v, tex_v), sprite_v) in land_row.iter().zip(tex_row.iter_mut()).zip(sprite_row) diff -r e2c51c8e0b2e -r a4c1a2d0ac24 rust/mapgen/src/theme.rs --- a/rust/mapgen/src/theme.rs Thu Nov 08 00:00:58 2018 +0100 +++ b/rust/mapgen/src/theme.rs Thu Nov 08 07:15:22 2018 +0300 @@ -38,7 +38,7 @@ } #[inline] - pub fn rows(&self) -> impl Iterator { + pub fn rows(&self) -> impl DoubleEndedIterator { self.pixels.rows() } @@ -46,16 +46,26 @@ pub fn get_row(&self, index: usize) -> &[u32] { &self.pixels[index] } + + #[inline] + pub fn get_pixel(&self, x: usize, y: usize) -> u32 { + self.pixels[y][x] + } } pub struct Theme { - land_texture: Option + land_texture: Option, + border_texture: Option } impl Theme { pub fn land_texture(&self) -> Option<&ThemeSprite> { self.land_texture.as_ref() } + + pub fn border_texture(&self) -> Option<&ThemeSprite> { + self.border_texture.as_ref() + } } #[derive(Debug)] @@ -80,7 +90,8 @@ impl Theme { pub fn new() -> Self { Theme { - land_texture: None + land_texture: None, + border_texture: None, } } @@ -90,23 +101,9 @@ for entry in read_dir(path)? { let file = entry?; if file.file_name() == "LandTex.png" { - let decoder = Decoder::new( - BufReader::new(File::open(file.path())?)); - let (info, mut reader) = decoder.read_info()?; - - if info.color_type != ColorType::RGBA { - return Err(ThemeLoadError::Format( - format!("Unexpected format: {:?}", info.color_type))); - } - let size = Size::new(info.width as usize, info.height as usize); - - let mut buffer: Vec2D = Vec2D::new(size, 0); - reader.next_frame(slice_u32_to_u8_mut(buffer.as_mut_slice()))?; - - let land_tex = ThemeSprite { - pixels: buffer - }; - theme.land_texture = Some(land_tex) + theme.land_texture = Some(load_sprite(&file.path())?) + } else if file.file_name() == "Border.png" { + theme.border_texture = Some(load_sprite(&file.path())?) } } @@ -114,6 +111,23 @@ } } +fn load_sprite(path: &Path) -> Result { + let decoder = Decoder::new( + BufReader::new(File::open(path)?)); + let (info, mut reader) = decoder.read_info()?; + + if info.color_type != ColorType::RGBA { + return Err(ThemeLoadError::Format( + format!("Unexpected format: {:?}", info.color_type))); + } + let size = Size::new(info.width as usize, info.height as usize); + + let mut pixels: Vec2D = Vec2D::new(size, 0); + reader.next_frame(slice_u32_to_u8_mut(pixels.as_mut_slice()))?; + + Ok(ThemeSprite { pixels }) +} + pub fn slice_u32_to_u8(slice_u32: &[u32]) -> &[u8] { unsafe { from_raw_parts::( diff -r e2c51c8e0b2e -r a4c1a2d0ac24 rust/vec2d/src/lib.rs --- a/rust/vec2d/src/lib.rs Thu Nov 08 00:00:58 2018 +0100 +++ b/rust/vec2d/src/lib.rs Thu Nov 08 07:15:22 2018 +0300 @@ -86,12 +86,12 @@ } #[inline] - pub fn rows(&self) -> impl Iterator { + pub fn rows(&self) -> impl DoubleEndedIterator { self.data.chunks(self.width()) } #[inline] - pub fn rows_mut(&mut self) -> impl Iterator { + pub fn rows_mut(&mut self) -> impl DoubleEndedIterator { let width = self.width(); self.data.chunks_mut(width) }