use super::{Graphics, opengl, FilterMode, Filter, WrapMode, Wrap, Image, validate_pixels};
use crate::error::{GameError, GameResult};
use crate::math::{Size, Region};
use crate::engine::Engine;
use glow::Context;
use std::rc::Rc;
use std::path::Path;
pub struct Texture {
texture: Rc<opengl::Texture>,
size: Size<u32>,
filter: Filter,
mipmap_generated: bool,
wrap: Wrap,
}
impl Texture {
pub fn new(graphics: &mut Graphics, size: impl Into<Size<u32>>, pixels: Option<&[u8]>) -> GameResult<Self> {
let size = size.into();
if let Some(pixels) = pixels {
validate_pixels(size, pixels)?;
}
let filter = graphics.default_filter();
let generate_mipmap = filter.mipmap.is_some();
let wrap = graphics.default_wrap();
let texture = opengl::Texture::new(graphics.gl())
.map_err(|error| GameError::InitError(error.into()))?;
texture.bind();
texture.init_image(size.width, size.height, pixels);
texture.set_filter(filter);
if generate_mipmap {
texture.generate_mipmap();
}
texture.set_wrap(wrap);
texture.unbind();
Ok(Self {
texture: Rc::new(texture),
size,
filter,
mipmap_generated: generate_mipmap,
wrap,
})
}
pub fn from_image(graphics: &mut Graphics, image: &Image) -> GameResult<Self> {
let size = image.size();
let pixels = image.pixels();
Self::new(graphics, size, Some(pixels))
}
pub fn from_bytes(graphics: &mut Graphics, bytes: &[u8]) -> GameResult<Self> {
let image = Image::from_bytes(bytes)?;
Self::from_image(graphics, &image)
}
pub fn load(engine: &mut Engine, path: impl AsRef<Path>) -> GameResult<Self> {
let image = Image::load(engine, path)?;
Self::from_image(engine.graphics(), &image)
}
pub(crate) fn white_1_1(gl: Rc<Context>) -> GameResult<Rc<opengl::Texture>> {
let texture = opengl::Texture::new(gl)
.map_err(|error| GameError::InitError(error.into()))?;
texture.bind();
texture.init_image(1, 1, Some(&[255, 255, 255, 255]));
texture.set_filter(Filter::new(FilterMode::Nearest, FilterMode::Nearest, None));
texture.set_wrap(Wrap::uv(WrapMode::Repeat, WrapMode::Repeat));
texture.unbind();
Ok(Rc::new(texture))
}
pub(crate) fn for_font_cache(graphics: &mut Graphics, size: u32) -> GameResult<Self> {
let size = Size::new(size, size);
let filter = graphics.default_filter();
let generate_mipmap = filter.mipmap.is_some();
let wrap = Wrap::uv(WrapMode::Repeat, WrapMode::Repeat);
let texture = opengl::Texture::new(graphics.gl())
.map_err(|error| GameError::InitError(error.into()))?;
texture.bind();
texture.init_image(size.width, size.height, None);
texture.set_filter(filter);
if generate_mipmap {
texture.generate_mipmap();
}
texture.set_wrap(wrap);
texture.unbind();
Ok(Self {
texture: Rc::new(texture),
size,
filter,
mipmap_generated: generate_mipmap,
wrap,
})
}
pub(crate) fn texture(&self) -> &Rc<opengl::Texture> {
&self.texture
}
pub fn size(&self) -> Size<u32> {
self.size
}
pub fn filter(&self) -> Filter {
self.filter
}
pub fn set_filter(&mut self, filter: Filter) {
if self.filter != filter {
self.texture.bind();
self.texture.set_filter(filter);
if !self.mipmap_generated && filter.mipmap.is_some() {
self.texture.generate_mipmap();
self.mipmap_generated = true;
}
self.texture.unbind();
self.filter = filter;
}
}
pub fn wrap(&self) -> Wrap {
self.wrap
}
pub fn set_wrap(&mut self, wrap: Wrap) {
if self.wrap != wrap {
self.texture.bind();
self.texture.set_wrap(wrap);
self.texture.unbind();
self.wrap = wrap;
}
}
pub fn init_pixels(&mut self, size: impl Into<Size<u32>>, pixels: Option<&[u8]>) -> GameResult {
let size = size.into();
if let Some(pixels) = pixels {
validate_pixels(size, pixels)?;
}
self.texture.bind();
self.texture.init_image(size.width, size.height, pixels);
self.size = size;
if self.filter.mipmap.is_some() {
self.texture.generate_mipmap();
self.mipmap_generated = true;
} else {
self.mipmap_generated = false;
}
self.texture.unbind();
Ok(())
}
pub fn init_with_image(&mut self, image: &Image) -> GameResult {
let size = image.size();
let pixels = image.pixels();
self.init_pixels(size, Some(pixels))
}
pub fn update_pixels(&mut self, region: impl Into<Region<u32>>, pixels: Option<&[u8]>) -> GameResult {
let region = region.into();
if let Some(pixels) = pixels {
validate_pixels(region.size(), pixels)?;
}
self.texture.bind();
self.texture.sub_image(
region.x,
region.y,
region.width,
region.height,
pixels,
);
if self.filter.mipmap.is_some() {
self.texture.generate_mipmap();
self.mipmap_generated = true;
} else {
self.mipmap_generated = false;
}
self.texture.unbind();
Ok(())
}
pub fn resize(&mut self, size: impl Into<Size<u32>>) {
let size = size.into();
self.texture.bind();
self.texture.init_image(size.width, size.height, None);
self.size = size;
if self.filter.mipmap.is_some() {
self.texture.generate_mipmap();
self.mipmap_generated = true;
} else {
self.mipmap_generated = false;
}
self.texture.unbind();
}
}