Files
a0_basic_app
a1_vehicle
a2_async_sim
ab_glyph
ab_glyph_rasterizer
adler
adler32
agents
aho_corasick
anyhow
approx
aquamarine
ash
atty
bitflags
bytemuck
byteorder
cache_padded
cfg_if
chrono
color_quant
crc32fast
crossbeam_channel
crossbeam_deque
crossbeam_epoch
crossbeam_utils
deflate
draw2d
either
flexi_logger
generic_array
gif
glfw
glfw_sys
glob
image
indoc
itertools
jpeg_decoder
lazy_static
libc
libloading
log
matrixmultiply
memchr
memoffset
miniz_oxide
nalgebra
base
geometry
linalg
third_party
num_complex
num_cpus
num_integer
num_iter
num_rational
num_traits
owned_ttf_parser
paste
png
proc_macro2
proc_macro_error
proc_macro_error_attr
quote
raw_window_handle
rawpointer
rayon
rayon_core
regex
regex_syntax
scoped_threadpool
scopeguard
semver
semver_parser
serde
serde_derive
simba
smawk
spin_sleep
syn
terminal_size
textwrap
thiserror
thiserror_impl
tiff
time
triple_buffer
ttf_parser
typenum
unicode_width
unicode_xid
unindent
vk_sys
weezl
yansi
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
use super::Texture2dFactory;

use crate::graphics::{
    vulkan::{
        buffer::CpuBuffer,
        texture::{MipmapExtent, TextureImage},
    },
    Graphics,
};

use anyhow::Result;
use ash::vk;
use image::ImageBuffer;
use std::path::Path;

/// Types which implement this trait can load 2d textures from files on the
/// disk.
pub trait TextureLoader {
    /// Read a file from the local filesystem into memory as a usable texture.
    fn read_texture_file(
        &self,
        file_path: impl Into<String>,
    ) -> Result<TextureImage>;
}

impl TextureLoader for Graphics {
    /// Read a file from the local filesystem into memory as a usable texture.
    fn read_texture_file(
        &self,
        file_path: impl Into<String>,
    ) -> Result<TextureImage> {
        let path_string = file_path.into();

        let mipmaps = read_file_mipmaps(&path_string)?;
        let packed_mipmap_data: Vec<&[u8]> = mipmaps
            .iter()
            .map(|mipmap| mipmap.as_raw() as &[u8])
            .collect();

        let mut texture = self.create_empty_2d_texture(
            path_string,
            mipmaps[0].width(),
            mipmaps[0].height(),
            mipmaps.len() as u32,
        )?;

        let mut transfer_buffer = CpuBuffer::new(
            self.device.clone(),
            vk::BufferUsageFlags::TRANSFER_SRC,
        )?;

        unsafe {
            transfer_buffer.write_data_arrays(&packed_mipmap_data)?;

            let mipmap_sizes: Vec<MipmapExtent> = mipmaps
                .iter()
                .map(|mipmap| MipmapExtent {
                    width: mipmap.width(),
                    height: mipmap.height(),
                })
                .collect();

            texture
                .upload_mipmaps_from_buffer(&transfer_buffer, &mipmap_sizes)?;
        }
        Ok(texture)
    }
}

type ImageBufferU8 = ImageBuffer<image::Rgba<u8>, Vec<u8>>;

/// Read a file as an rgba8 image. Mipmaps are automatically generated based
/// on the file size and a Gaussian filter. The returned list is the set of
/// all image mipmaps in a R8G8B8A8 format.
fn read_file_mipmaps(path: &impl AsRef<Path>) -> Result<Vec<ImageBufferU8>> {
    let image_file = image::open(path)?.into_rgba8();
    let (width, height) = (image_file.width(), image_file.height());
    let mip_levels = (height.max(width) as f32).log2().floor() as u32 + 1;

    let mut mipmaps = Vec::with_capacity(mip_levels as usize);
    mipmaps.push(image_file.clone());
    for mipmap_level in 1..mip_levels {
        use image::imageops;
        let mipmap = imageops::resize(
            &image_file,
            width >> mipmap_level,
            height >> mipmap_level,
            imageops::FilterType::Gaussian,
        );
        mipmaps.push(mipmap);
    }

    Ok(mipmaps)
}