sf2g/graphics/
texture.rs

1use crate::{
2    IntoSfResult, SfError, SfResult,
3    cpp::FBox,
4    ffi::graphics::{self as ffi, sfTexture_create},
5    graphics::RenderWindow,
6    system::Vector2u,
7    window::Window,
8};
9
10decl_opaque! {
11/// [`Image`] living on the graphics card that can be used for drawing.
12///
13/// `Texture` stores pixels that can be drawn, with a sprite for example.
14///
15/// A texture lives in the graphics card memory, therefore it is very fast to draw a
16/// texture to a render target,
17/// or copy a render target to a texture (the graphics card can access both directly).
18///
19/// Being stored in the graphics card memory has some drawbacks.
20/// A texture cannot be manipulated as freely as a [`Image`],
21/// you need to prepare the pixels first and then upload them to the texture in a
22/// single operation (see the various update methods below).
23///
24/// `Texture` makes it easy to convert from/to [`Image`],
25/// but keep in mind that these calls require transfers between the graphics card and
26/// the central memory, therefore they are slow operations.
27///
28/// A texture can be loaded from an image, but also directly from a file/memory/stream.
29/// The necessary shortcuts are defined so that you don't need an image first for the
30/// most common cases.
31/// However, if you want to perform some modifications on the pixels before creating the
32/// final texture, you can load your file to a [`Image`], do whatever you need with the pixels,
33/// and then call [`Texture::load_from_image`].
34///
35/// Since they live in the graphics card memory,
36/// the pixels of a texture cannot be accessed without a slow copy first.
37/// And they cannot be accessed individually.
38/// Therefore, if you need to read the texture's pixels (like for pixel-perfect collisions),
39/// it is recommended to store the collision information separately,
40/// for example in an array of booleans.
41///
42/// Like [`Image`], `Texture` can handle a unique internal representation of pixels,
43/// which is RGBA 32 bits.
44/// This means that a pixel must be composed of
45/// 8 bits red, green, blue and alpha channels – just like a [`Color`].
46///
47/// [`Color`]: crate::graphics::Color
48pub Texture;
49}
50
51/// Creation and loading
52impl Texture {
53    /// Create the texture.
54    ///
55    /// If this function fails, the texture is left unchanged.
56    ///
57    /// Returns whether creation was successful.
58    #[must_use = "Check if texture was created successfully"]
59    pub fn create(&mut self, width: u32, height: u32) -> SfResult<()> {
60        unsafe { sfTexture_create(self, width, height) }.into_sf_result()
61    }
62    /// Creates a new `Texture`
63    pub fn new() -> SfResult<FBox<Texture>> {
64        FBox::new(unsafe { ffi::sfTexture_new() }).into_sf_result()
65    }
66
67    /// Load texture from memory
68    ///
69    /// The `area` argument can be used to load only a sub-rectangle of the whole image.
70    /// If you want the entire image then use a default [`IntRect`].
71    /// If the area rectangle crosses the bounds of the image,
72    /// it is adjusted to fit the image size.
73    ///
74    /// # Arguments
75    /// * mem - Pointer to the file data in memory
76    /// * area - Area of the image to load
77    pub fn load_from_memory(&mut self, mem: &[u8]) -> SfResult<()> {
78        let img = image::load_from_memory(mem).map_err(|_| SfError::CallFailed)?;
79        self.create(img.width(), img.height())?;
80        self.update_from_pixels(&img.to_rgba8(), img.width(), img.height(), 0, 0);
81        Ok(())
82    }
83
84    /// Load texture from a file
85    ///
86    /// # Arguments
87    /// * filename - Path of the image file to load
88    pub fn load_from_file(&mut self, filename: &str) -> SfResult<()> {
89        let img = image::open(filename).map_err(|_| SfError::CallFailed)?;
90        self.create(img.width(), img.height())?;
91        self.update_from_pixels(&img.to_rgba8(), img.width(), img.height(), 0, 0);
92        Ok(())
93    }
94
95    /// Convenience method to easily create and load a `Texture` from a file.
96    pub fn from_file(filename: &str) -> SfResult<FBox<Self>> {
97        let mut new = Self::new()?;
98        new.load_from_file(filename)?;
99        Ok(new)
100    }
101}
102
103/// Query properties
104impl Texture {
105    /// Return the size of the texture
106    ///
107    /// Return the Size in pixels
108    #[must_use]
109    pub fn size(&self) -> Vector2u {
110        unsafe { ffi::sfTexture_getSize(self) }
111    }
112    /// Get the maximum texture size allowed
113    ///
114    /// Return the maximum size allowed for textures, in pixels
115    #[must_use]
116    pub fn maximum_size() -> u32 {
117        unsafe { ffi::sfTexture_getMaximumSize() }
118    }
119    /// Tell whether the smooth filter is enabled or not for a texture
120    ///
121    /// Return true if smoothing is enabled, false if it is disabled
122    #[must_use]
123    pub fn is_smooth(&self) -> bool {
124        unsafe { ffi::sfTexture_isSmooth(self) }
125    }
126    /// Tell whether a texture is repeated or not
127    ///
128    /// Return frue if repeat mode is enabled, false if it is disabled
129    #[must_use]
130    pub fn is_repeated(&self) -> bool {
131        unsafe { ffi::sfTexture_isRepeated(self) }
132    }
133    /// Tell whether the texture source is converted from sRGB or not.
134    #[must_use]
135    pub fn is_srgb(&self) -> bool {
136        unsafe { ffi::sfTexture_isSrgb(self) }
137    }
138}
139
140/// Set properties
141impl Texture {
142    /// Enable or disable the smooth filter on a texture
143    ///
144    /// # Arguments
145    /// * smooth - true to enable smoothing, false to disable it
146    pub fn set_smooth(&mut self, smooth: bool) {
147        unsafe { ffi::sfTexture_setSmooth(self, smooth) }
148    }
149
150    /// Enable or disable repeating for a texture
151    ///
152    /// epeating is involved when using texture coordinates
153    /// outside the texture rectangle [0, 0, width, height].
154    /// In this case, if repeat mode is enabled, the whole texture
155    /// will be repeated as many times as needed to reach the
156    /// coordinate (for example, if the X texture coordinate is
157    /// 3 * width, the texture will be repeated 3 times).
158    /// If repeat mode is disabled, the "extra space" will instead
159    /// be filled with border pixels.
160    /// Warning: on very old graphics cards, white pixels may appear
161    /// when the texture is repeated. With such cards, repeat mode
162    /// can be used reliably only if the texture has power-of-two
163    /// dimensions (such as 256x128).
164    /// Repeating is disabled by default.
165    ///
166    /// # Arguments
167    /// * repeated  - true to repeat the texture, false to disable repeating
168    pub fn set_repeated(&mut self, repeated: bool) {
169        unsafe { ffi::sfTexture_setRepeated(self, repeated) }
170    }
171    /// Enable or disable conversion from sRGB.
172    ///
173    /// When providing texture data from an image file or memory, it can either be stored in a
174    /// linear color space or an sRGB color space. Most digital images account for gamma correction
175    /// already, so they would need to be "uncorrected" back to linear color space before being
176    /// processed by the hardware. The hardware can automatically convert it from the sRGB
177    /// color space to a linear color space when it gets sampled. When the rendered image gets
178    /// output to the final framebuffer, it gets converted back to sRGB.
179    ///
180    /// After enabling or disabling sRGB conversion, make sure to reload the texture data in
181    /// order for the setting to take effect.
182    ///
183    /// This option is only useful in conjunction with an sRGB capable framebuffer.
184    /// This can be requested during window creation.
185    pub fn set_srgb(&mut self, srgb: bool) {
186        unsafe { ffi::sfTexture_setSrgb(self, srgb) }
187    }
188}
189
190/// OpenGL interop
191impl Texture {
192    /// Get the underlying OpenGL handle of the texture.
193    ///
194    /// You shouldn't need to use this function, unless you have very specific stuff to implement
195    /// that SFML doesn't support, or implement a temporary workaround until a bug is fixed.
196    #[must_use]
197    pub fn native_handle(&self) -> u32 {
198        unsafe { ffi::sfTexture_getNativeHandle(self) }
199    }
200
201    /// Bind a texture for rendering
202    ///
203    /// This function is not part of the graphics API, it mustn't be
204    /// used when drawing SFML entities. It must be used only if you
205    /// mix `Texture` with OpenGL code.
206    pub fn bind(&self) {
207        unsafe { ffi::sfTexture_bind(self) }
208    }
209}
210
211/// Copying and updating
212impl Texture {
213    /// Update a part of the texture from the contents of a window.
214    ///
215    /// This function does nothing if either the texture or the window was not previously created.
216    ///
217    /// # Safety
218    /// No additional check is performed on the size of the window, passing an invalid combination
219    /// of window size and offset will lead to an _undefined behavior_.
220    pub unsafe fn update_from_window(&mut self, window: &Window, x: u32, y: u32) {
221        unsafe { ffi::sfTexture_updateFromWindow(self, window, x, y) }
222    }
223
224    /// Update a part of the texture from the contents of a render window.
225    ///
226    /// This function does nothing if either the texture or the window was not previously created.
227    ///
228    /// # Safety
229    /// No additional check is performed on the size of the window, passing an invalid combination
230    /// of window size and offset will lead to an _undefined behavior_.
231    pub unsafe fn update_from_render_window(
232        &mut self,
233        render_window: &RenderWindow,
234        x: u32,
235        y: u32,
236    ) {
237        unsafe { ffi::sfTexture_updateFromRenderWindow(self, render_window, x, y) }
238    }
239
240    /// Update a part of this texture from another texture.
241    ///
242    /// This function does nothing if either texture was not previously created.
243    ///
244    /// # Safety
245    /// No additional check is performed on the size of the texture,
246    /// passing an invalid combination of texture size and offset will
247    /// lead to an _undefined behavior_.
248    pub unsafe fn update_from_texture(&mut self, texture: &Texture, x: u32, y: u32) {
249        unsafe { ffi::sfTexture_updateFromTexture(self, texture, x, y) }
250    }
251
252    /// Update a part of the texture from an array of pixels.
253    ///
254    /// The size of the pixel array must match the width and height arguments,
255    /// and it must contain 32-bits RGBA pixels.
256    ///
257    /// This function does nothing if the texture was not previously created.
258    ///
259    /// # Panics
260    ///
261    /// Panics the provided parameters would result in out of bounds access.
262    pub fn update_from_pixels(&mut self, pixels: &[u8], width: u32, height: u32, x: u32, y: u32) {
263        let my_dims = self.size();
264        assert!(
265            x + width <= my_dims.x
266                && y + height <= my_dims.y
267                && pixels.len() == (width * height * 4) as usize
268        );
269        unsafe { ffi::sfTexture_updateFromPixels(self, pixels.as_ptr(), width, height, x, y) }
270    }
271
272    /// Swap the contents of this texture with those of another.
273    pub fn swap(&mut self, other: &mut Texture) {
274        unsafe { ffi::sfTexture_swap(self, other) }
275    }
276}
277
278/// Mipmapping
279impl Texture {
280    /// Generate a mipmap using the current texture data.
281    ///
282    /// Mipmaps are pre-computed chains of optimized textures. Each level of texture in a mipmap
283    /// is generated by halving each of the previous level's dimensions. This is done until the
284    /// final level has the size of 1x1. The textures generated in this process may make use of
285    /// more advanced filters which might improve the visual quality of textures when they are
286    /// applied to objects much smaller than they are. This is known as minification. Because
287    /// fewer texels (texture elements) have to be sampled from when heavily minified, usage of
288    /// mipmaps can also improve rendering performance in certain scenarios.
289    ///
290    /// Mipmap generation relies on the necessary OpenGL extension being available.
291    /// If it is unavailable or generation fails due to another reason, this function will return
292    /// false. Mipmap data is only valid from the time it is generated until the next time the base
293    /// level image is modified, at which point this function will have to be called again to
294    /// regenerate it.
295    pub fn generate_mipmap(&mut self) -> SfResult<()> {
296        unsafe { ffi::sfTexture_generateMipmap(self) }.into_sf_result()
297    }
298}
299
300impl ToOwned for Texture {
301    type Owned = FBox<Texture>;
302
303    fn to_owned(&self) -> Self::Owned {
304        FBox::new(unsafe { ffi::sfTexture_cpy(self) }).expect("Failed to copy texture")
305    }
306}
307
308impl Drop for Texture {
309    fn drop(&mut self) {
310        unsafe { ffi::sfTexture_del(self) }
311    }
312}