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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
use std::sync::Arc;

use crate::{
    emath::NumExt, mutex::RwLock, textures::TextureOptions, ImageData, ImageDelta, TextureId,
    TextureManager,
};

/// Used to paint images.
///
/// An _image_ is pixels stored in RAM, and represented using [`ImageData`].
/// Before you can paint it however, you need to convert it to a _texture_.
///
/// If you are using egui, use `egui::Context::load_texture`.
///
/// The [`TextureHandle`] can be cloned cheaply.
/// When the last [`TextureHandle`] for specific texture is dropped, the texture is freed.
///
/// See also [`TextureManager`].
#[must_use]
pub struct TextureHandle {
    tex_mngr: Arc<RwLock<TextureManager>>,
    id: TextureId,
}

impl Drop for TextureHandle {
    fn drop(&mut self) {
        self.tex_mngr.write().free(self.id);
    }
}

impl Clone for TextureHandle {
    fn clone(&self) -> Self {
        self.tex_mngr.write().retain(self.id);
        Self {
            tex_mngr: self.tex_mngr.clone(),
            id: self.id,
        }
    }
}

impl PartialEq for TextureHandle {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        self.id == other.id
    }
}

impl Eq for TextureHandle {}

impl std::hash::Hash for TextureHandle {
    #[inline]
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        self.id.hash(state);
    }
}

impl TextureHandle {
    /// If you are using egui, use `egui::Context::load_texture` instead.
    pub fn new(tex_mngr: Arc<RwLock<TextureManager>>, id: TextureId) -> Self {
        Self { tex_mngr, id }
    }

    #[inline]
    pub fn id(&self) -> TextureId {
        self.id
    }

    /// Assign a new image to an existing texture.
    #[allow(clippy::needless_pass_by_ref_mut)] // Intentionally hide interiority of mutability
    pub fn set(&mut self, image: impl Into<ImageData>, options: TextureOptions) {
        self.tex_mngr
            .write()
            .set(self.id, ImageDelta::full(image.into(), options));
    }

    /// Assign a new image to a subregion of the whole texture.
    #[allow(clippy::needless_pass_by_ref_mut)] // Intentionally hide interiority of mutability
    pub fn set_partial(
        &mut self,
        pos: [usize; 2],
        image: impl Into<ImageData>,
        options: TextureOptions,
    ) {
        self.tex_mngr
            .write()
            .set(self.id, ImageDelta::partial(pos, image.into(), options));
    }

    /// width x height
    pub fn size(&self) -> [usize; 2] {
        self.tex_mngr
            .read()
            .meta(self.id)
            .map_or([0, 0], |tex| tex.size)
    }

    /// width x height
    pub fn size_vec2(&self) -> crate::Vec2 {
        let [w, h] = self.size();
        crate::Vec2::new(w as f32, h as f32)
    }

    /// `width x height x bytes_per_pixel`
    pub fn byte_size(&self) -> usize {
        self.tex_mngr
            .read()
            .meta(self.id)
            .map_or(0, |tex| tex.bytes_used())
    }

    /// width / height
    pub fn aspect_ratio(&self) -> f32 {
        let [w, h] = self.size();
        w as f32 / h.at_least(1) as f32
    }

    /// Debug-name.
    pub fn name(&self) -> String {
        self.tex_mngr
            .read()
            .meta(self.id)
            .map_or_else(|| "<none>".to_owned(), |tex| tex.name.clone())
    }
}

impl From<&TextureHandle> for TextureId {
    #[inline(always)]
    fn from(handle: &TextureHandle) -> Self {
        handle.id()
    }
}

impl From<&mut TextureHandle> for TextureId {
    #[inline(always)]
    fn from(handle: &mut TextureHandle) -> Self {
        handle.id()
    }
}