Skip to main content

gl_utils/
texture.rs

1//! Texture related utilities.
2
3use std;
4use glium;
5use image;
6use log;
7
8/// An RGBA 16x16 square of pixels with white color values with an opaque cross
9/// of two pixels thickness
10pub const CROSSHAIR_PNG_FILE_BYTES                        : &[u8; 101] =
11  include_bytes!("../crosshair.png");
12/// For use with `BLEND_FUNC_INVERT_COLOR`-- transparent pixels have *black*
13/// color values instead of white but the opaque portion is still white
14pub const CROSSHAIR_INVERSE_PNG_FILE_BYTES                : &[u8; 95]  =
15  include_bytes!("../crosshair-inverse.png");
16pub const TILESET_EASCII_ACORN_8X8_PNG_FILE_BYTES         : &[u8; 2538] =
17  include_bytes!("../tileset_eascii_acorn-bbc-micro_8x8.png");
18pub const TILESET_EASCII_ACORN_8X8_INVERSE_PNG_FILE_BYTES : &[u8; 2526] =
19  include_bytes!("../tileset_eascii_acorn-bbc-micro_8x8-inverse.png");
20pub const TILESET_EASCII_ACORN_16X16_PNG_FILE_BYTES       : &[u8; 3427] =
21  include_bytes!("../tileset_eascii_acorn-bbc-micro_16x16.png");
22pub const TILESET_EASCII_ACORN_16X16_INVERSE_PNG_FILE_BYTES : &[u8; 3427] =
23  include_bytes!("../tileset_eascii_acorn-bbc-micro_16x16-inverse.png");
24pub const POINTER_HAND_PNG_FILE_BYTES_OFFSET : (&[u8; 183], [i16; 2]) =
25  (include_bytes!("../pointer-hand.png"), [3, -10]);
26
27#[derive(Debug)]
28pub enum LoadError {
29  IoError              (std::io::Error),
30  ImageError           (image::ImageError),
31  TextureCreationError (glium::texture::TextureCreationError)
32}
33
34/// Load a 2D texture from the given bytes with the given format and mipmaps.
35pub fn texture2d_with_mipmaps_from_bytes (
36  glium_facade : &dyn glium::backend::Facade,
37  bytes        : &[u8],
38  image_format : image::ImageFormat,
39  mipmaps      : glium::texture::MipmapsOption
40) -> Result <glium::Texture2d, LoadError> {
41  let img = image::load_from_memory_with_format (bytes, image_format)?
42    .to_rgba8();
43  let img_dimensions = img.dimensions();
44  log::debug!(dimensions:?=img_dimensions; "texture load bytes");
45  let raw_image_2d = glium::texture::RawImage2d::from_raw_rgba_reversed (
46    img.into_raw().as_slice(),
47    img_dimensions);
48
49  glium::Texture2d::with_mipmaps (glium_facade, raw_image_2d, mipmaps)
50    .map_err (Into::into)
51}
52
53/// Load a 2D texture from the given path with the given format and mipmaps.
54pub fn texture2d_with_mipmaps_from_file (
55  glium_facade : &dyn glium::backend::Facade,
56  filepath     : &'static str,
57  image_format : image::ImageFormat,
58  mipmaps      : glium::texture::MipmapsOption
59) -> Result <glium::Texture2d, LoadError> {
60  log::debug!(filepath:?; "texture load file");
61  let bytes = std::fs::read (filepath)?;
62  texture2d_with_mipmaps_from_bytes (
63    glium_facade, bytes.as_slice(), image_format, mipmaps)
64}
65
66/// Load a 2D texture array from the given vector of byte slices for each
67/// individual texture, with the given format and mipmaps.
68pub fn texture2darray_with_mipmaps_from_bytes (
69  glium_facade : &dyn glium::backend::Facade,
70  bytes_slices : &[&[u8]],
71  image_format : image::ImageFormat,
72  mipmaps      : glium::texture::MipmapsOption
73) -> Result <glium::texture::Texture2dArray, LoadError> {
74  let raw_images = {
75    let mut v = Vec::with_capacity (bytes_slices.len());
76    for bytes in bytes_slices {
77      let img = image::load_from_memory_with_format (bytes, image_format)?
78        .to_rgba8();
79      let img_dimensions = img.dimensions();
80      log::debug!(dimensions:?=img_dimensions; "texture array load bytes");
81      let raw_image_2d = glium::texture::RawImage2d::from_raw_rgba_reversed (
82        img.into_raw().as_slice(),
83        img_dimensions);
84      v.push (raw_image_2d);
85    }
86    v
87  };
88
89  glium::texture::Texture2dArray::with_mipmaps (
90    glium_facade, raw_images, mipmaps
91  ).map_err (Into::into)
92}
93
94/// Load a 2D texture array from the given paths with the given format and
95/// mipmaps.
96pub fn texture2darray_with_mipmaps_from_files (
97  glium_facade : &dyn glium::backend::Facade,
98  filepaths    : &[&'static str],
99  image_format : image::ImageFormat,
100  mipmaps      : glium::texture::MipmapsOption
101) -> Result <glium::texture::Texture2dArray, LoadError> {
102  if filepaths.is_empty() {
103    return Err (std::io::Error::new (std::io::ErrorKind::InvalidInput,
104      "no input paths provided").into())
105  }
106
107  let bytes = {
108    let mut v = Vec::with_capacity (filepaths.len());
109    v.resize_with (filepaths.len(), Default::default);
110    for (i, filepath) in filepaths.iter().enumerate() {
111      log::debug!(filepath:?; "texture load file");
112      v[i] = std::fs::read (filepath)?;
113    }
114    v
115  };
116
117  let bytes_vec = {
118    let mut v = Vec::with_capacity (filepaths.len());
119    for bytes in bytes.iter() {
120      v.push (bytes.as_slice());
121    }
122    v
123  };
124
125  let texture2darray = texture2darray_with_mipmaps_from_bytes (
126    glium_facade, &bytes_vec, image_format, mipmaps)?;
127  // bytes must live until here
128  Ok (texture2darray)
129}
130
131//
132//  impls
133//
134impl std::fmt::Display for LoadError {
135  fn fmt (&self, f : &mut std::fmt::Formatter) -> std::fmt::Result {
136    match self {
137      LoadError::IoError              (err) => write!(f, "I/O error: {err}"),
138      LoadError::ImageError           (err) => err.fmt (f),
139      LoadError::TextureCreationError (err) => err.fmt (f)
140    }
141  }
142}
143
144impl std::error::Error for LoadError {
145  fn source (&self) -> Option <&(dyn std::error::Error + 'static)> {
146    match self {
147      LoadError::IoError              (err) => err.source(),
148      LoadError::ImageError           (err) => err.source(),
149      LoadError::TextureCreationError (err) => err.source()
150    }
151  }
152}
153
154impl From <std::io::Error> for LoadError {
155  fn from (err : std::io::Error) -> Self {
156    LoadError::IoError (err)
157  }
158}
159
160impl From <image::ImageError> for LoadError {
161  fn from (err : image::ImageError) -> Self {
162    LoadError::ImageError (err)
163  }
164}
165
166impl From <glium::texture::TextureCreationError> for LoadError {
167  fn from (err : glium::texture::TextureCreationError) -> Self {
168    LoadError::TextureCreationError (err)
169  }
170}