rust/mapgen/src/theme.rs
author Wuzzy <Wuzzy2@mail.ru>
Mon, 16 Sep 2019 17:33:49 +0200
changeset 15431 8504fee3b601
parent 15141 febccab419b1
child 15944 c571d4b8879c
child 15957 0710feb75c99
permissions -rw-r--r--
Racer: Fix weird water splashes after waypoint placement Does not affect official racer, as only waypoint placement is touched. The reason was that the air attack gear sometimes was not deleted fast enough so it might occassionally drop some air bombs (these are deleted now). Also, the airplane position was set to water level, which caused another water splash.

use png::{ColorType, Decoder, DecodingError};
use std::{
    fs::{read_dir, File},
    io,
    io::BufReader,
    path::Path,
    slice::{from_raw_parts, from_raw_parts_mut},
};

use integral_geometry::Size;
use vec2d::Vec2D;

pub struct ThemeSprite {
    pixels: Vec2D<u32>,
}

impl ThemeSprite {
    #[inline]
    pub fn size(&self) -> Size {
        self.pixels.size()
    }

    #[inline]
    pub fn width(&self) -> usize {
        self.size().width
    }

    #[inline]
    pub fn height(&self) -> usize {
        self.size().height
    }

    #[inline]
    pub fn rows(&self) -> impl DoubleEndedIterator<Item = &[u32]> {
        self.pixels.rows()
    }

    #[inline]
    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 fn to_transposed(&self) -> ThemeSprite {
        let size = self.size().transpose();
        let mut pixels = Vec2D::new(size, 0u32);
        for (y, row) in self.pixels.rows().enumerate() {
            for (x, v) in row.iter().enumerate() {
                pixels[x][y] = *v;
            }
        }
        ThemeSprite { pixels }
    }

    pub fn to_tiled(&self) -> TiledSprite {
        let size = self.size();
        assert!(size.is_power_of_two());
        let tile_width_shift = size.width.trailing_zeros() as usize + 2;
        let mut pixels = vec![0u32; size.area()];

        for (y, row) in self.pixels.rows().enumerate() {
            for (x, v) in row.iter().enumerate() {
                pixels[get_tiled_index(x, y, tile_width_shift)] = *v;
            }
        }

        TiledSprite {
            tile_width_shift,
            size,
            pixels,
        }
    }
}

#[inline]
fn get_tiled_index(x: usize, y: usize, tile_width_shift: usize) -> usize {
    (((y >> 2) << tile_width_shift) + ((x >> 2) << 4)) + ((y & 0b11) << 2) + (x & 0b11)
}

pub struct TiledSprite {
    tile_width_shift: usize,
    size: Size,
    pixels: Vec<u32>,
}

impl TiledSprite {
    #[inline]
    pub fn size(&self) -> Size {
        self.size
    }

    #[inline]
    pub fn width(&self) -> usize {
        self.size().width
    }

    #[inline]
    pub fn height(&self) -> usize {
        self.size().height
    }

    #[inline]
    pub fn get_pixel(&self, x: usize, y: usize) -> u32 {
        self.pixels[get_tiled_index(x, y, self.tile_width_shift)]
    }
}

pub struct Theme {
    land_texture: Option<ThemeSprite>,
    border_texture: Option<ThemeSprite>,
}

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)]
pub enum ThemeLoadError {
    File(io::Error),
    Decoding(DecodingError),
    Format(String),
}

impl From<io::Error> for ThemeLoadError {
    fn from(e: io::Error) -> Self {
        ThemeLoadError::File(e)
    }
}

impl From<DecodingError> for ThemeLoadError {
    fn from(e: DecodingError) -> Self {
        ThemeLoadError::Decoding(e)
    }
}

impl Theme {
    pub fn new() -> Self {
        Theme {
            land_texture: None,
            border_texture: None,
        }
    }

    pub fn load(path: &Path) -> Result<Theme, ThemeLoadError> {
        let mut theme = Self::new();

        for entry in read_dir(path)? {
            let file = entry?;
            if file.file_name() == "LandTex.png" {
                theme.land_texture = Some(load_sprite(&file.path())?)
            } else if file.file_name() == "Border.png" {
                theme.border_texture = Some(load_sprite(&file.path())?)
            }
        }

        Ok(theme)
    }
}

fn load_sprite(path: &Path) -> Result<ThemeSprite, ThemeLoadError> {
    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<u32> = 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::<u8>(slice_u32.as_ptr() as *const u8, slice_u32.len() * 4) }
}

pub fn slice_u32_to_u8_mut(slice_u32: &mut [u32]) -> &mut [u8] {
    unsafe { from_raw_parts_mut::<u8>(slice_u32.as_mut_ptr() as *mut u8, slice_u32.len() * 4) }
}