Skip to main content

fyrox_texture/
lib.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21//! Texture is an image that used to fill faces to add details to them.
22//!
23//! In most cases textures are just 2D images, however there are some exclusions to that -
24//! for example cube maps, that may be used for environment mapping. Fyrox supports 1D, 2D,
25//! 3D and Cube textures.
26//!
27//! ## Supported formats
28//!
29//! To load images and decode them, Fyrox uses image and ddsfile crates. Here is the list of
30//! supported formats: png, tga, bmp, dds, jpg, gif, tiff, dds.
31//!
32//! ## Compressed textures
33//!
34//! Fyrox supports most commonly used formats of compressed textures: DXT1, DXT3, DXT5.
35//!
36//! ## Render target
37//!
38//! Texture can be used as render target to render scene in it. To do this you should use
39//! new_render_target method and pass its result to scene's render target property. Renderer
40//! will automatically provide you info about metrics of texture, but it won't give you
41//! access to pixels of render target.
42
43use ddsfile::{Caps2, D3DFormat};
44use fast_image_resize as fr;
45use fast_image_resize::ResizeOptions;
46use fxhash::FxHasher;
47use fyrox_core::visitor::error::VisitError;
48use fyrox_core::visitor::pod::PodVecView;
49use fyrox_core::{
50    algebra::{Vector2, Vector3},
51    futures::io::Error,
52    io::FileError,
53    num_traits::Bounded,
54    reflect::prelude::*,
55    sparse::AtomicIndex,
56    uuid,
57    uuid::Uuid,
58    uuid_provider,
59    visitor::{Visit, VisitResult, Visitor},
60    TypeUuidProvider,
61};
62use fyrox_resource::{
63    embedded_data_source, io::ResourceIo, manager::BuiltInResource, options::ImportOptions,
64    untyped::ResourceKind, Resource, ResourceData,
65};
66use image::{ColorType, DynamicImage, ImageError, ImageFormat, Pixel};
67use serde::{Deserialize, Serialize};
68use std::sync::LazyLock;
69use std::{
70    fmt::{Debug, Display, Formatter},
71    hash::{Hash, Hasher},
72    io::Cursor,
73    ops::{Deref, DerefMut, Shr},
74    path::Path,
75    sync::Arc,
76};
77use strum_macros::{AsRefStr, EnumString, VariantNames};
78
79pub mod loader;
80
81/// Texture kind.
82#[derive(Copy, Clone, Debug, Reflect, AsRefStr, EnumString, VariantNames, TypeUuidProvider)]
83#[type_uuid(id = "542eb785-875b-43ce-b73a-a25024535f48")]
84pub enum TextureKind {
85    /// 1D texture.
86    Line {
87        /// Length of the texture.
88        length: u32,
89    },
90    /// 2D texture.
91    Rectangle {
92        /// Width of the texture.
93        width: u32,
94        /// Height of the texture.
95        height: u32,
96    },
97    /// Cube texture.
98    Cube {
99        /// Width and height of the cube face.
100        size: u32,
101    },
102    /// Volume texture (3D).
103    Volume {
104        /// Width of the volume.
105        width: u32,
106        /// Height of the volume.
107        height: u32,
108        /// Depth of the volume.
109        depth: u32,
110    },
111}
112
113impl TextureKind {
114    /// Tries to fetch [`TextureKind::Line`]'s length.
115    #[inline]
116    pub fn line_length(&self) -> Option<u32> {
117        if let Self::Line { length } = self {
118            Some(*length)
119        } else {
120            None
121        }
122    }
123
124    /// Tries to fetch [`TextureKind::Rectangle`]'s width (x) and height (y).
125    #[inline]
126    pub fn rectangle_size(&self) -> Option<Vector2<u32>> {
127        if let Self::Rectangle { width, height } = self {
128            Some(Vector2::new(*width, *height))
129        } else {
130            None
131        }
132    }
133
134    /// Tries to fetch [`TextureKind::Cube`]'s size.
135    #[inline]
136    pub fn cube_size(&self) -> Option<u32> {
137        if let Self::Cube { size } = self {
138            Some(*size)
139        } else {
140            None
141        }
142    }
143
144    /// Tries to fetch [`TextureKind::Volume`]'s width (x), height (y), depth (z).
145    #[inline]
146    pub fn volume_size(&self) -> Option<Vector3<u32>> {
147        if let Self::Volume {
148            width,
149            height,
150            depth,
151        } = self
152        {
153            Some(Vector3::new(*width, *height, *depth))
154        } else {
155            None
156        }
157    }
158}
159
160impl Default for TextureKind {
161    fn default() -> Self {
162        Self::Rectangle {
163            width: 0,
164            height: 0,
165        }
166    }
167}
168
169impl Visit for TextureKind {
170    fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
171        let mut region = visitor.enter_region(name)?;
172
173        let mut id = match self {
174            TextureKind::Line { .. } => 0,
175            TextureKind::Rectangle { .. } => 1,
176            TextureKind::Cube { .. } => 2,
177            TextureKind::Volume { .. } => 3,
178        };
179        id.visit("Id", &mut region)?;
180        if region.is_reading() {
181            *self = match id {
182                0 => TextureKind::Line { length: 0 },
183                1 => TextureKind::Rectangle {
184                    width: 0,
185                    height: 0,
186                },
187                2 => TextureKind::Cube { size: 0 },
188                3 => TextureKind::Volume {
189                    width: 0,
190                    height: 0,
191                    depth: 0,
192                },
193                _ => {
194                    return VisitResult::Err(VisitError::User(format!(
195                        "Invalid texture kind {id}!"
196                    )))
197                }
198            };
199        }
200        match self {
201            TextureKind::Line { length } => {
202                length.visit("Length", &mut region)?;
203            }
204            TextureKind::Rectangle { width, height } => {
205                width.visit("Width", &mut region)?;
206                height.visit("Height", &mut region)?;
207            }
208            TextureKind::Cube { size } => {
209                // Called `Width` for backward compatibility reasons.
210                size.visit("Width", &mut region)?;
211            }
212            TextureKind::Volume {
213                width,
214                height,
215                depth,
216            } => {
217                width.visit("Width", &mut region)?;
218                height.visit("Height", &mut region)?;
219                depth.visit("Depth", &mut region)?;
220            }
221        }
222
223        Ok(())
224    }
225}
226
227/// Data storage of a texture.
228#[derive(Default, Clone, Reflect)]
229pub struct TextureBytes(Vec<u8>);
230
231impl Visit for TextureBytes {
232    fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
233        self.0.visit(name, visitor)
234    }
235}
236
237impl Debug for TextureBytes {
238    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
239        write!(f, "Texture has {} bytes", self.0.len())
240    }
241}
242
243impl From<Vec<u8>> for TextureBytes {
244    fn from(bytes: Vec<u8>) -> Self {
245        Self(bytes)
246    }
247}
248
249impl Deref for TextureBytes {
250    type Target = Vec<u8>;
251
252    fn deref(&self) -> &Self::Target {
253        &self.0
254    }
255}
256
257impl DerefMut for TextureBytes {
258    fn deref_mut(&mut self) -> &mut Self::Target {
259        &mut self.0
260    }
261}
262
263/// Actual texture data.
264#[derive(Debug, Clone, Reflect)]
265pub struct Texture {
266    kind: TextureKind,
267    bytes: TextureBytes,
268    pixel_kind: TexturePixelKind,
269    minification_filter: TextureMinificationFilter,
270    magnification_filter: TextureMagnificationFilter,
271    s_wrap_mode: TextureWrapMode,
272    t_wrap_mode: TextureWrapMode,
273    r_wrap_mode: TextureWrapMode,
274    base_level: usize,
275    max_level: usize,
276    min_lod: f32,
277    max_lod: f32,
278    lod_bias: f32,
279    mip_count: u32,
280    anisotropy: f32,
281    modifications_counter: u64,
282    sampler_properties_modifications: u64,
283    is_render_target: bool,
284    #[doc(hidden)]
285    #[reflect(hidden)]
286    pub cache_index: Arc<AtomicIndex>,
287}
288
289impl TypeUuidProvider for Texture {
290    fn type_uuid() -> Uuid {
291        uuid!("02c23a44-55fa-411a-bc39-eb7a5eadf15c")
292    }
293}
294
295impl ResourceData for Texture {
296    fn type_uuid(&self) -> Uuid {
297        <Self as TypeUuidProvider>::type_uuid()
298    }
299
300    fn save(&mut self, path: &Path) -> Result<(), Box<dyn std::error::Error>> {
301        let color_type = match self.pixel_kind {
302            TexturePixelKind::R8 => ColorType::L8,
303            TexturePixelKind::Luminance8 => ColorType::L8,
304            TexturePixelKind::RGB8 | TexturePixelKind::SRGB8 => ColorType::Rgb8,
305            TexturePixelKind::RGBA8 | TexturePixelKind::SRGBA8 => ColorType::Rgba8,
306            TexturePixelKind::RG8 => ColorType::La8,
307            TexturePixelKind::LuminanceAlpha8 => ColorType::La8,
308            TexturePixelKind::R16 => ColorType::L16,
309            TexturePixelKind::Luminance16 => ColorType::L16,
310            TexturePixelKind::RG16 => ColorType::La16,
311            TexturePixelKind::LuminanceAlpha16 => ColorType::La16,
312            TexturePixelKind::RGB16 => ColorType::Rgb16,
313            TexturePixelKind::RGBA16 => ColorType::Rgba16,
314            TexturePixelKind::RGB32F => ColorType::Rgb32F,
315            TexturePixelKind::RGBA32F => ColorType::Rgba32F,
316            TexturePixelKind::DXT1RGB
317            | TexturePixelKind::DXT1RGBA
318            | TexturePixelKind::DXT3RGBA
319            | TexturePixelKind::DXT5RGBA
320            | TexturePixelKind::R8RGTC
321            | TexturePixelKind::RG8RGTC
322            | TexturePixelKind::BGR8
323            | TexturePixelKind::BGRA8
324            | TexturePixelKind::RGB16F
325            | TexturePixelKind::R32F
326            | TexturePixelKind::R16F => return Err(Box::new(TextureError::UnsupportedFormat)),
327        };
328        if let TextureKind::Rectangle { width, height } = self.kind {
329            Ok(image::save_buffer(
330                path,
331                self.bytes.as_ref(),
332                width,
333                height,
334                color_type,
335            )?)
336        } else {
337            Err(Box::new(TextureError::UnsupportedFormat))
338        }
339    }
340
341    fn can_be_saved(&self) -> bool {
342        true
343    }
344
345    fn try_clone_box(&self) -> Option<Box<dyn ResourceData>> {
346        Some(Box::new(self.clone()))
347    }
348}
349
350impl Visit for Texture {
351    fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
352        let mut region = visitor.enter_region(name)?;
353
354        let mut kind = self.pixel_kind.id();
355        kind.visit("KindId", &mut region)?;
356        if region.is_reading() {
357            self.pixel_kind = TexturePixelKind::new(kind)?;
358        }
359
360        self.minification_filter
361            .visit("MinificationFilter", &mut region)?;
362        self.magnification_filter
363            .visit("MagnificationFilter", &mut region)?;
364        self.anisotropy.visit("Anisotropy", &mut region)?;
365        self.s_wrap_mode.visit("SWrapMode", &mut region)?;
366        self.t_wrap_mode.visit("TWrapMode", &mut region)?;
367        self.t_wrap_mode.visit("RWrapMode", &mut region)?;
368        self.mip_count.visit("MipCount", &mut region)?;
369        self.kind.visit("Kind", &mut region)?;
370        let mut bytes_view = PodVecView::from_pod_vec(&mut self.bytes);
371        bytes_view.visit("Data", &mut region)?;
372        self.base_level.visit("BaseLevel", &mut region)?;
373        self.max_level.visit("MaxLevel", &mut region)?;
374        self.min_lod.visit("MinLod", &mut region)?;
375        self.max_lod.visit("MaxLod", &mut region)?;
376        self.lod_bias.visit("LodBias", &mut region)?;
377
378        Ok(())
379    }
380}
381
382impl Default for Texture {
383    /// It is very important to mention that defaults may be different for texture when you're
384    /// importing them through resource manager, see
385    /// [TextureImportOptions](../engine/resource_manager/struct.TextureImportOptions.html) for more info.
386    fn default() -> Self {
387        Self {
388            kind: TextureKind::Rectangle {
389                width: 0,
390                height: 0,
391            },
392            bytes: Default::default(),
393            pixel_kind: TexturePixelKind::RGBA8,
394            minification_filter: TextureMinificationFilter::LinearMipMapLinear,
395            magnification_filter: TextureMagnificationFilter::Linear,
396            s_wrap_mode: TextureWrapMode::Repeat,
397            t_wrap_mode: TextureWrapMode::Repeat,
398            r_wrap_mode: TextureWrapMode::Repeat,
399            base_level: 0,
400            max_level: default_max_level(),
401            min_lod: default_min_lod(),
402            max_lod: default_max_lod(),
403            lod_bias: 0.0,
404            mip_count: 1,
405            anisotropy: 16.0,
406            modifications_counter: 0,
407            sampler_properties_modifications: 1,
408            is_render_target: false,
409            cache_index: Default::default(),
410        }
411    }
412}
413
414/// A filter for mip-map generation.
415#[derive(
416    Default, Copy, Clone, Deserialize, Serialize, Debug, Reflect, AsRefStr, EnumString, VariantNames,
417)]
418pub enum MipFilter {
419    /// Simple nearest filter, it is the fastest filter available, but it produces noisy mip levels and
420    /// in most cases it is not advised to use it. Consider its performance as 1x.
421    Nearest,
422    /// Bilinear filtration. It has good balance between image quality and speed. It is ~13x times slower
423    /// than [`Self::Nearest`]. It is default filtering method.
424    #[default]
425    Bilinear,
426    /// Hamming filtration. It has much nicer filtration quality than Bilinear, but the same performance.
427    Hamming,
428    /// Catmull-Rom spline filtration. It has very good filtration quality, but it is ~23x times slower
429    /// than [`Self::Nearest`].
430    CatmullRom,
431    /// Gaussian filtration. It has perfect filtration quality, but it is ~37x times slower than
432    /// [`Self::Nearest`].
433    Lanczos,
434}
435
436uuid_provider!(MipFilter = "8fa17c0e-6889-4540-b396-97db4dc952aa");
437
438impl MipFilter {
439    fn into_filter_type(self) -> fr::FilterType {
440        match self {
441            MipFilter::Nearest => fr::FilterType::Box,
442            MipFilter::Bilinear => fr::FilterType::Bilinear,
443            MipFilter::CatmullRom => fr::FilterType::CatmullRom,
444            MipFilter::Hamming => fr::FilterType::Hamming,
445            MipFilter::Lanczos => fr::FilterType::Lanczos3,
446        }
447    }
448}
449
450/// Allows you to define a set of parameters for a texture resource.
451///
452/// # Details
453///
454/// Usually the content of this structure is stored in a separate file with .options extension. Typical content of
455/// a settings file should look like this:
456///
457/// ```text
458/// (
459///     minification_filter: Linear,
460///     magnification_filter: Linear,
461///     s_wrap_mode: Repeat,
462///     t_wrap_mode: ClampToEdge,
463///     anisotropy: 8.0,
464///     compression: NoCompression,
465/// )
466/// ```
467#[derive(Clone, Deserialize, Serialize, Debug, Reflect)]
468pub struct TextureImportOptions {
469    #[serde(default)]
470    pub(crate) minification_filter: TextureMinificationFilter,
471    #[serde(default)]
472    pub(crate) magnification_filter: TextureMagnificationFilter,
473    #[serde(default)]
474    pub(crate) s_wrap_mode: TextureWrapMode,
475    #[serde(default)]
476    pub(crate) t_wrap_mode: TextureWrapMode,
477    #[serde(default)]
478    pub(crate) r_wrap_mode: TextureWrapMode,
479    #[serde(default)]
480    pub(crate) anisotropy: f32,
481    #[serde(default)]
482    pub(crate) compression: CompressionOptions,
483    #[serde(default)]
484    pub(crate) mip_filter: MipFilter,
485    #[serde(default)]
486    pub(crate) flip_green_channel: bool,
487    #[serde(default)]
488    pub(crate) base_level: usize,
489    #[serde(default = "default_max_level")]
490    pub(crate) max_level: usize,
491    #[serde(default = "default_min_lod")]
492    pub(crate) min_lod: f32,
493    #[serde(default = "default_max_lod")]
494    pub(crate) max_lod: f32,
495    #[serde(default)]
496    pub(crate) lod_bias: f32,
497}
498
499fn default_max_level() -> usize {
500    1000
501}
502
503fn default_min_lod() -> f32 {
504    -1000.0
505}
506
507fn default_max_lod() -> f32 {
508    1000.0
509}
510
511impl Default for TextureImportOptions {
512    fn default() -> Self {
513        Self {
514            minification_filter: TextureMinificationFilter::LinearMipMapLinear,
515            magnification_filter: TextureMagnificationFilter::Linear,
516            s_wrap_mode: TextureWrapMode::Repeat,
517            t_wrap_mode: TextureWrapMode::Repeat,
518            r_wrap_mode: TextureWrapMode::Repeat,
519            anisotropy: 16.0,
520            compression: CompressionOptions::default(),
521            mip_filter: Default::default(),
522            flip_green_channel: false,
523            base_level: 0,
524            max_level: default_max_level(),
525            min_lod: default_min_lod(),
526            max_lod: default_max_lod(),
527            lod_bias: 0.0,
528        }
529    }
530}
531
532impl ImportOptions for TextureImportOptions {}
533
534impl TextureImportOptions {
535    /// Sets new minification filter which will be applied to every imported texture as
536    /// default value.
537    pub fn with_minification_filter(
538        mut self,
539        minification_filter: TextureMinificationFilter,
540    ) -> Self {
541        self.minification_filter = minification_filter;
542        self
543    }
544
545    /// Sets new minification filter which will be applied to every imported texture as
546    /// default value.
547    pub fn set_minification_filter(&mut self, minification_filter: TextureMinificationFilter) {
548        self.minification_filter = minification_filter;
549    }
550
551    /// Sets new magnification filter which will be applied to every imported texture as
552    /// default value.
553    pub fn with_magnification_filter(
554        mut self,
555        magnification_filter: TextureMagnificationFilter,
556    ) -> Self {
557        self.magnification_filter = magnification_filter;
558        self
559    }
560
561    /// Sets new magnification filter which will be applied to every imported texture as
562    /// default value.
563    pub fn set_magnification_filter(&mut self, magnification_filter: TextureMagnificationFilter) {
564        self.magnification_filter = magnification_filter;
565    }
566
567    /// Sets new S coordinate wrap mode which will be applied to every imported texture as
568    /// default value.
569    pub fn with_s_wrap_mode(mut self, s_wrap_mode: TextureWrapMode) -> Self {
570        self.s_wrap_mode = s_wrap_mode;
571        self
572    }
573
574    /// Sets new S coordinate wrap mode which will be applied to every imported texture as
575    /// default value.
576    pub fn set_s_wrap_mode(&mut self, s_wrap_mode: TextureWrapMode) {
577        self.s_wrap_mode = s_wrap_mode;
578    }
579
580    /// Sets new T coordinate wrap mode which will be applied to every imported texture as
581    /// default value.
582    pub fn with_t_wrap_mode(mut self, t_wrap_mode: TextureWrapMode) -> Self {
583        self.t_wrap_mode = t_wrap_mode;
584        self
585    }
586
587    /// Sets new T coordinate wrap mode which will be applied to every imported texture as
588    /// default value.
589    pub fn set_t_wrap_mode(&mut self, t_wrap_mode: TextureWrapMode) {
590        self.t_wrap_mode = t_wrap_mode;
591    }
592
593    /// Sets new anisotropy level which will be applied to every imported texture as
594    /// default value.
595    pub fn with_anisotropy(mut self, anisotropy: f32) -> Self {
596        self.anisotropy = anisotropy.min(1.0);
597        self
598    }
599
600    /// Sets new anisotropy level which will be applied to every imported texture as
601    /// default value.
602    pub fn set_anisotropy(&mut self, anisotropy: f32) {
603        self.anisotropy = anisotropy.min(1.0);
604    }
605
606    /// Sets desired texture compression.
607    pub fn with_compression(mut self, compression: CompressionOptions) -> Self {
608        self.compression = compression;
609        self
610    }
611
612    /// Sets desired texture compression.
613    pub fn set_compression(&mut self, compression: CompressionOptions) {
614        self.compression = compression;
615    }
616
617    /// Same effect as [`Texture::set_base_level`].
618    pub fn with_base_level(mut self, base_level: usize) -> Self {
619        self.base_level = base_level;
620        self
621    }
622
623    /// Same effect as [`Texture::set_base_level`].
624    pub fn set_base_level(&mut self, base_level: usize) {
625        self.base_level = base_level;
626    }
627
628    /// Same effect as [`Texture::set_max_level`].
629    pub fn with_max_level(mut self, max_level: usize) -> Self {
630        self.max_level = max_level;
631        self
632    }
633
634    /// Same effect as [`Texture::set_max_level`].
635    pub fn set_max_level(&mut self, max_level: usize) {
636        self.max_level = max_level;
637    }
638
639    /// Same effect as [`Texture::set_min_lod`].
640    pub fn with_min_lod(mut self, min_lod: f32) -> Self {
641        self.min_lod = min_lod;
642        self
643    }
644
645    /// Same effect as [`Texture::set_min_lod`].
646    pub fn set_min_lod(&mut self, min_lod: f32) {
647        self.min_lod = min_lod;
648    }
649
650    /// Same effect as [`Texture::set_max_lod`].
651    pub fn with_max_lod(mut self, max_lod: f32) -> Self {
652        self.max_lod = max_lod;
653        self
654    }
655
656    /// Same effect as [`Texture::set_max_lod`].
657    pub fn set_max_lod(&mut self, max_lod: f32) {
658        self.max_lod = max_lod;
659    }
660
661    /// Same effect as [`Texture::set_lod_bias`].
662    pub fn with_lod_bias(mut self, lod_bias: f32) -> Self {
663        self.lod_bias = lod_bias;
664        self
665    }
666
667    /// Same effect as [`Texture::set_lod_bias`].
668    pub fn set_lod_bias(&mut self, lod_bias: f32) {
669        self.lod_bias = lod_bias;
670    }
671}
672
673/// Placeholder texture.
674pub static PLACEHOLDER: LazyLock<BuiltInResource<Texture>> = LazyLock::new(|| {
675    BuiltInResource::new(
676        "__PlaceholderTexture",
677        embedded_data_source!("default.png"),
678        |data| {
679            TextureResource::load_from_memory(
680                uuid!("58b0e112-a21a-481f-b305-a2dc5a8bea1f"),
681                ResourceKind::External,
682                data,
683                Default::default(),
684            )
685            .unwrap()
686        },
687    )
688});
689
690/// Pure color texture.
691pub static PURE_COLOR: LazyLock<BuiltInResource<Texture>> = LazyLock::new(|| {
692    BuiltInResource::new(
693        "__PureColorTexture",
694        embedded_data_source!("pure_color.png"),
695        |data| {
696            TextureResource::load_from_memory(
697                uuid!("9709eef2-305c-44da-91e5-6f293d74408a"),
698                ResourceKind::External,
699                data,
700                Default::default(),
701            )
702            .unwrap()
703        },
704    )
705});
706
707/// Type alias for texture resources.
708pub type TextureResource = Resource<Texture>;
709
710/// Extension trait for texture resources.
711pub trait TextureResourceExtension: Sized {
712    /// Creates new render target. This method automatically configures GPU texture
713    /// to correct settings, after render target was created, it must not be modified, otherwise
714    /// result is undefined.
715    fn new_render_target(width: u32, height: u32) -> Self;
716
717    /// Creates a new cube map render target. This method automatically configures GPU texture
718    /// to correct settings. After the render target was created, it must not be modified. Otherwise
719    /// the result is undefined. Cube map contains six images
720    fn new_cube_render_target(resolution: u32) -> Self;
721
722    /// Creates new render target with the specified pixel kind. This method automatically configures GPU texture
723    /// to correct settings, after render target was created, it must not be modified, otherwise
724    /// result is undefined.
725    fn new_render_target_with_format(width: u32, height: u32, pixel_kind: TexturePixelKind)
726        -> Self;
727
728    /// Tries to load a texture from given data. Use this method if you want to
729    /// load a texture from embedded data.
730    ///
731    /// # On-demand compression
732    ///
733    /// The data can be compressed if needed to improve performance on GPU side.
734    ///
735    /// # Important notes
736    ///
737    /// Textures loaded with this method won't be correctly serialized! It means
738    /// that if you'll made a scene with textures loaded with this method, and then
739    /// save a scene, then the engine won't be able to restore the textures if you'll
740    /// try to load the saved scene. This is essential limitation of this method,
741    /// because the engine does not know where to get the data of the texture at
742    /// loading. You should use `ResourceManager::request_texture` in majority of cases!
743    ///
744    /// Main use cases for this method are: procedural textures, icons for GUI.
745    fn load_from_memory(
746        resource_uuid: Uuid,
747        kind: ResourceKind,
748        data: &[u8],
749        import_options: TextureImportOptions,
750    ) -> Result<Self, TextureError>;
751
752    /// Tries to create new texture from given parameters, it may fail only if size of data passed
753    /// in does not match with required.
754    fn from_bytes(
755        resource_uuid: Uuid,
756        kind: TextureKind,
757        pixel_kind: TexturePixelKind,
758        bytes: Vec<u8>,
759        resource_kind: ResourceKind,
760    ) -> Option<Self>;
761
762    /// Creates a deep clone of the texture. Unlike [`TextureResource::clone`], this method clones the actual texture data,
763    /// which could be slow.
764    fn deep_clone(&self) -> Self;
765}
766
767impl TextureResourceExtension for TextureResource {
768    fn new_render_target(width: u32, height: u32) -> Self {
769        Self::new_render_target_with_format(width, height, TexturePixelKind::RGBA8)
770    }
771
772    fn new_cube_render_target(size: u32) -> Self {
773        Resource::new_ok(
774            Default::default(),
775            Default::default(),
776            Texture {
777                // Render target will automatically set width and height before rendering.
778                kind: TextureKind::Cube { size },
779                bytes: Default::default(),
780                pixel_kind: TexturePixelKind::RGBA8,
781                minification_filter: TextureMinificationFilter::Linear,
782                magnification_filter: TextureMagnificationFilter::Linear,
783                s_wrap_mode: TextureWrapMode::Repeat,
784                t_wrap_mode: TextureWrapMode::Repeat,
785                r_wrap_mode: TextureWrapMode::Repeat,
786                base_level: 0,
787                max_level: 1000,
788                min_lod: -1000.0,
789                max_lod: 1000.0,
790                lod_bias: 0.0,
791                mip_count: 1,
792                anisotropy: 1.0,
793                modifications_counter: 0,
794                sampler_properties_modifications: 1,
795                is_render_target: true,
796                cache_index: Default::default(),
797            },
798        )
799    }
800
801    fn new_render_target_with_format(
802        width: u32,
803        height: u32,
804        pixel_kind: TexturePixelKind,
805    ) -> Self {
806        Resource::new_ok(
807            Default::default(),
808            Default::default(),
809            Texture {
810                // Render target will automatically set width and height before rendering.
811                kind: TextureKind::Rectangle { width, height },
812                bytes: Default::default(),
813                pixel_kind,
814                minification_filter: TextureMinificationFilter::Linear,
815                magnification_filter: TextureMagnificationFilter::Linear,
816                s_wrap_mode: TextureWrapMode::Repeat,
817                t_wrap_mode: TextureWrapMode::Repeat,
818                r_wrap_mode: TextureWrapMode::Repeat,
819                base_level: 0,
820                max_level: 1000,
821                min_lod: -1000.0,
822                max_lod: 1000.0,
823                lod_bias: 0.0,
824                mip_count: 1,
825                anisotropy: 1.0,
826                modifications_counter: 0,
827                sampler_properties_modifications: 1,
828                is_render_target: true,
829                cache_index: Default::default(),
830            },
831        )
832    }
833
834    fn load_from_memory(
835        resource_uuid: Uuid,
836        kind: ResourceKind,
837        data: &[u8],
838        import_options: TextureImportOptions,
839    ) -> Result<Self, TextureError> {
840        Ok(Resource::new_ok(
841            resource_uuid,
842            kind,
843            Texture::load_from_memory(data, import_options)?,
844        ))
845    }
846
847    fn from_bytes(
848        resource_uuid: Uuid,
849        kind: TextureKind,
850        pixel_kind: TexturePixelKind,
851        bytes: Vec<u8>,
852        resource_kind: ResourceKind,
853    ) -> Option<Self> {
854        Some(Resource::new_ok(
855            resource_uuid,
856            resource_kind,
857            Texture::from_bytes(kind, pixel_kind, bytes)?,
858        ))
859    }
860
861    fn deep_clone(&self) -> Self {
862        let kind = self.header().kind;
863        let data = self.data_ref().clone();
864        Resource::new_ok(Uuid::new_v4(), kind, data)
865    }
866}
867
868/// The texture magnification function is used when the pixel being textured maps to an area
869/// less than or equal to one texture element.
870#[derive(
871    Copy,
872    Clone,
873    Debug,
874    Hash,
875    PartialOrd,
876    PartialEq,
877    Deserialize,
878    Serialize,
879    Reflect,
880    VariantNames,
881    EnumString,
882    AsRefStr,
883    Visit,
884    Eq,
885)]
886#[repr(u32)]
887#[derive(Default)]
888pub enum TextureMagnificationFilter {
889    /// Returns the value of the texture element that is nearest to the center of the pixel
890    /// being textured.
891    Nearest = 0,
892
893    /// Returns the weighted average of the four texture elements that are closest to the
894    /// center of the pixel being textured.
895    #[default]
896    Linear = 1,
897}
898
899uuid_provider!(TextureMagnificationFilter = "824f5b6c-8957-42db-9ebc-ef2a5dece5ab");
900
901/// The texture minifying function is used whenever the pixel being textured maps to an area
902/// greater than one texture element.
903#[derive(
904    Copy,
905    Clone,
906    Debug,
907    Hash,
908    PartialOrd,
909    PartialEq,
910    Deserialize,
911    Serialize,
912    Reflect,
913    VariantNames,
914    EnumString,
915    AsRefStr,
916    Visit,
917    Eq,
918)]
919#[repr(u32)]
920#[derive(Default)]
921pub enum TextureMinificationFilter {
922    /// Returns the value of the texture element that is nearest to the center of the pixel
923    /// being textured.
924    Nearest = 0,
925
926    /// Chooses the mipmap that most closely matches the size of the pixel being textured and
927    /// uses the Nearest criterion (the texture element nearest to the center of the pixel)
928    /// to produce a texture value.
929    NearestMipMapNearest = 1,
930
931    /// Chooses the two mipmaps that most closely match the size of the pixel being textured
932    /// and uses the Nearest criterion (the texture element nearest to the center of the pixel)
933    /// to produce a texture value from each mipmap. The final texture value is a weighted average
934    /// of those two values.
935    NearestMipMapLinear = 2,
936
937    /// Returns the weighted average of the four texture elements that are closest to the
938    /// center of the pixel being textured.
939    Linear = 3,
940
941    /// Chooses the mipmap that most closely matches the size of the pixel being textured and
942    /// uses the Linear criterion (a weighted average of the four texture elements that are
943    /// closest to the center of the pixel) to produce a texture value.
944    LinearMipMapNearest = 4,
945
946    /// Chooses the two mipmaps that most closely match the size of the pixel being textured
947    /// and uses the Linear criterion (a weighted average of the four texture elements that
948    /// are closest to the center of the pixel) to produce a texture value from each mipmap.
949    /// The final texture value is a weighted average of those two values.
950    #[default]
951    LinearMipMapLinear = 5,
952}
953
954uuid_provider!(TextureMinificationFilter = "0ec9e072-6d0a-47b2-a9c2-498cac4de22b");
955
956impl TextureMinificationFilter {
957    /// Returns true if minification filter is using mip mapping, false - otherwise.
958    pub fn is_using_mip_mapping(self) -> bool {
959        match self {
960            TextureMinificationFilter::Nearest | TextureMinificationFilter::Linear => false,
961            TextureMinificationFilter::NearestMipMapNearest
962            | TextureMinificationFilter::LinearMipMapLinear
963            | TextureMinificationFilter::NearestMipMapLinear
964            | TextureMinificationFilter::LinearMipMapNearest => true,
965        }
966    }
967}
968
969/// Defines a law of texture coordinate modification.
970#[derive(
971    Copy,
972    Clone,
973    Debug,
974    Hash,
975    PartialOrd,
976    PartialEq,
977    Deserialize,
978    Serialize,
979    Reflect,
980    VariantNames,
981    EnumString,
982    AsRefStr,
983    Visit,
984    Eq,
985)]
986#[repr(u32)]
987#[derive(Default)]
988pub enum TextureWrapMode {
989    /// Causes the integer part of a coordinate to be ignored; GPU uses only the fractional part,
990    /// thereby creating a repeating pattern.
991    #[default]
992    Repeat = 0,
993
994    /// Causes a coordinates to be clamped to the range range, where N is the size of the texture
995    /// in the direction of clamping
996    ClampToEdge = 1,
997
998    /// Evaluates a coordinates in a similar manner to ClampToEdge. However, in cases where clamping
999    /// would have occurred in ClampToEdge mode, the fetched texel data is substituted with the values
1000    /// specified by border color.
1001    ClampToBorder = 2,
1002
1003    /// Causes the a coordinate to be set to the fractional part of the texture coordinate if the integer
1004    /// part of coordinate is even; if the integer part of coordinate is odd, then the coordinate texture
1005    /// coordinate is set to 1-frac, where frac represents the fractional part of coordinate.
1006    MirroredRepeat = 3,
1007
1008    /// Causes a coordinate to be repeated as for MirroredRepeat for one repetition of the texture, at
1009    /// which point the coordinate to be clamped as in ClampToEdge.
1010    MirrorClampToEdge = 4,
1011}
1012
1013uuid_provider!(TextureWrapMode = "e360d139-4374-4323-a66d-d192809d9d87");
1014
1015/// Texture kind defines pixel format of texture.
1016#[derive(
1017    Copy,
1018    Clone,
1019    PartialEq,
1020    Eq,
1021    Debug,
1022    Reflect,
1023    Visit,
1024    AsRefStr,
1025    EnumString,
1026    VariantNames,
1027    TypeUuidProvider,
1028)]
1029#[type_uuid(id = "dcca9b9c-dd1e-412c-922f-074703d35781")]
1030#[repr(u32)]
1031pub enum TexturePixelKind {
1032    /// 1 byte red.
1033    R8 = 0,
1034
1035    /// Red, green, and blue components, each by 1 byte.
1036    RGB8 = 1,
1037
1038    /// Red, green, blue, and alpha components, each by 1 byte.
1039    RGBA8 = 2,
1040
1041    /// Red and green, each by 1 byte.
1042    RG8 = 3,
1043
1044    /// 2 byte red.
1045    R16 = 4,
1046
1047    /// Red and green, each by 2 byte.
1048    RG16 = 5,
1049
1050    /// Blue, green, and red components, each by 1 byte.
1051    BGR8 = 6,
1052
1053    /// Blue, green, red and alpha components, each by 1 byte.
1054    BGRA8 = 7,
1055
1056    /// Red, green, and blue components, each by 2 byte.
1057    RGB16 = 8,
1058
1059    /// Red, green, blue, and alpha components, each by 2 byte.
1060    RGBA16 = 9,
1061
1062    /// Compressed S3TC DXT1 RGB (no alpha).
1063    DXT1RGB = 10,
1064
1065    /// Compressed S3TC DXT1 RGBA.
1066    DXT1RGBA = 11,
1067
1068    /// Compressed S3TC DXT3 RGBA.
1069    DXT3RGBA = 12,
1070
1071    /// Compressed S3TC DXT5 RGBA.
1072    DXT5RGBA = 13,
1073
1074    /// Compressed R8 texture (RGTC).
1075    R8RGTC = 14,
1076
1077    /// Compressed RG8 texture (RGTC).
1078    RG8RGTC = 15,
1079
1080    /// Floating-point RGB texture with 32bit depth.
1081    RGB32F = 16,
1082
1083    /// Floating-point RGBA texture with 32bit depth.
1084    RGBA32F = 17,
1085
1086    /// 1 byte luminance texture where pixels will have (L, L, L, 1.0) value on fetching.
1087    ///
1088    /// # Platform-specific
1089    ///
1090    /// - WebAssembly - not supported, the image will act like [`Self::R8`] format, which
1091    ///   will have (R, 0.0, 0.0, 1.0) pixels.
1092    Luminance8 = 18,
1093
1094    /// 1 byte for luminance and 1 for alpha, where all pixels will have (L, L, L, A) value on fetching.
1095    ///
1096    /// # Platform-specific
1097    ///
1098    /// - WebAssembly - not supported, the image will act like [`Self::RG8`] format, which
1099    ///   will have (R, G, R, G) pixels.
1100    LuminanceAlpha8 = 19,
1101
1102    /// 2 byte luminance texture where pixels will have (L, L, L, 1.0) value on fetching.
1103    ///
1104    /// # Platform-specific
1105    ///
1106    /// - WebAssembly - not supported, the image will act like [`Self::R8`] format, which
1107    ///   will have (R, 0.0, 0.0, 1.0) pixels.
1108    Luminance16 = 20,
1109
1110    /// 2 byte for luminance and 2 for alpha, where all pixels will have (L, L, L, A) value on fetching.
1111    ///
1112    /// # Platform-specific
1113    ///
1114    /// - WebAssembly - not supported, the image will act like [`Self::RG16`] format, which
1115    ///   will have (R, G, R, G) pixels.
1116    LuminanceAlpha16 = 21,
1117
1118    /// Red, green, blue components, each by 2 byte half-precision float.
1119    RGB16F = 22,
1120
1121    /// Red component as 4-byte, medium-precision float.
1122    R32F = 23,
1123
1124    /// Red component as 2-byte, half-precision float.
1125    R16F = 24,
1126
1127    SRGBA8 = 25,
1128    SRGB8 = 26,
1129}
1130
1131impl TexturePixelKind {
1132    fn new(id: u32) -> Result<Self, String> {
1133        match id {
1134            0 => Ok(Self::R8),
1135            1 => Ok(Self::RGB8),
1136            2 => Ok(Self::RGBA8),
1137            3 => Ok(Self::RG8),
1138            4 => Ok(Self::R16),
1139            5 => Ok(Self::RG16),
1140            6 => Ok(Self::BGR8),
1141            7 => Ok(Self::BGRA8),
1142            8 => Ok(Self::RGB16),
1143            9 => Ok(Self::RGBA16),
1144            10 => Ok(Self::DXT1RGB),
1145            11 => Ok(Self::DXT1RGBA),
1146            12 => Ok(Self::DXT3RGBA),
1147            13 => Ok(Self::DXT5RGBA),
1148            14 => Ok(Self::R8RGTC),
1149            15 => Ok(Self::RG8RGTC),
1150            16 => Ok(Self::RGB32F),
1151            17 => Ok(Self::RGBA32F),
1152            18 => Ok(Self::Luminance8),
1153            19 => Ok(Self::LuminanceAlpha8),
1154            20 => Ok(Self::Luminance16),
1155            21 => Ok(Self::LuminanceAlpha16),
1156            22 => Ok(Self::RGB16F),
1157            23 => Ok(Self::R32F),
1158            24 => Ok(Self::R16F),
1159            25 => Ok(Self::SRGBA8),
1160            26 => Ok(Self::SRGB8),
1161            _ => Err(format!("Invalid texture kind {id}!")),
1162        }
1163    }
1164
1165    fn id(self) -> u32 {
1166        self as u32
1167    }
1168
1169    /// Tries to get size of the pixel in bytes. Pixels of compressed textures consumes less than a byte, so
1170    /// there's no way to express their size on whole number of bytes, in this case `None` is returned.
1171    pub fn size_in_bytes(&self) -> Option<usize> {
1172        match self {
1173            Self::R8 | Self::Luminance8 => Some(1),
1174            Self::RGB8 | Self::SRGB8 | Self::BGR8 => Some(3),
1175            Self::RGBA8
1176            | Self::SRGBA8
1177            | Self::RG16
1178            | Self::BGRA8
1179            | Self::LuminanceAlpha16
1180            | Self::R32F => Some(4),
1181            Self::RG8 | Self::R16 | Self::LuminanceAlpha8 | Self::Luminance16 | Self::R16F => {
1182                Some(2)
1183            }
1184            Self::RGB16 | Self::RGB16F => Some(6),
1185            Self::RGBA16 => Some(8),
1186            Self::RGB32F => Some(12),
1187            Self::RGBA32F => Some(16),
1188            // Pixels of compressed textures consumes less than a byte, so there's no way to express
1189            // their size on whole number of bytes.
1190            Self::DXT1RGB
1191            | Self::DXT1RGBA
1192            | Self::DXT3RGBA
1193            | Self::DXT5RGBA
1194            | Self::R8RGTC
1195            | Self::RG8RGTC => None,
1196        }
1197    }
1198}
1199
1200/// An error that may occur during texture operations.
1201#[derive(Debug)]
1202pub enum TextureError {
1203    /// Format (pixel format, dimensions) is not supported.
1204    UnsupportedFormat,
1205    /// An io error.
1206    Io(std::io::Error),
1207    /// Internal image crate error.
1208    Image(image::ImageError),
1209    /// An error occurred during file loading.
1210    FileLoadError(FileError),
1211}
1212
1213impl Display for TextureError {
1214    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1215        match self {
1216            TextureError::UnsupportedFormat => {
1217                write!(f, "Unsupported format!")
1218            }
1219            TextureError::Io(v) => {
1220                write!(f, "An i/o error has occurred: {v}")
1221            }
1222            TextureError::Image(v) => {
1223                write!(f, "Image loading error {v}")
1224            }
1225            TextureError::FileLoadError(v) => {
1226                write!(f, "A file load error has occurred {v:?}")
1227            }
1228        }
1229    }
1230}
1231
1232impl std::error::Error for TextureError {}
1233
1234impl From<FileError> for TextureError {
1235    fn from(v: FileError) -> Self {
1236        Self::FileLoadError(v)
1237    }
1238}
1239
1240impl From<image::ImageError> for TextureError {
1241    fn from(v: ImageError) -> Self {
1242        Self::Image(v)
1243    }
1244}
1245
1246impl From<std::io::Error> for TextureError {
1247    fn from(v: Error) -> Self {
1248        Self::Io(v)
1249    }
1250}
1251
1252fn ceil_div_4(x: u32) -> u32 {
1253    x.div_ceil(4)
1254}
1255
1256/// Texture compression options.
1257///
1258/// # Notes
1259///
1260/// Try to avoid using compression for normal maps, normals maps usually has smooth
1261/// gradients, but compression algorithms used by Fyrox cannot preserve good quality
1262/// of such gradients.
1263#[derive(
1264    Copy,
1265    Clone,
1266    Deserialize,
1267    Serialize,
1268    PartialEq,
1269    Eq,
1270    Debug,
1271    Reflect,
1272    VariantNames,
1273    EnumString,
1274    AsRefStr,
1275)]
1276#[repr(u32)]
1277#[derive(Default)]
1278pub enum CompressionOptions {
1279    /// An image will be stored without compression if it is not already compressed.
1280    #[default]
1281    NoCompression = 0,
1282
1283    /// An image will be encoded via DXT1 (BC1) compression with low quality if is not
1284    /// already compressed.
1285    /// Compression ratio is 1:8 (without alpha) or 1:6 (with 1-bit alpha).
1286    /// This option provides maximum speed by having lowest requirements of memory
1287    /// bandwidth.
1288    Speed = 1,
1289
1290    /// An image will be encoded via DXT5 (BC5) compression with high quality if it is
1291    /// not already compressed.
1292    /// Compression ratio is 1:4 (including alpha)
1293    /// This option is faster than `NoCompression` speed by lower requirements of memory
1294    /// bandwidth.
1295    Quality = 2,
1296}
1297
1298uuid_provider!(CompressionOptions = "fbdcc081-d0b8-4b62-9925-2de6c013fbf5");
1299
1300fn transmute_slice<T>(bytes: &[u8]) -> &'_ [T] {
1301    // SAFETY: This is absolutely safe because `image` crate's Rgb8/Rgba8/etc. and `tbc`s Rgb8/Rgba8/etc.
1302    // have exactly the same memory layout.
1303    unsafe {
1304        std::slice::from_raw_parts(
1305            bytes.as_ptr() as *const T,
1306            bytes.len() / std::mem::size_of::<T>(),
1307        )
1308    }
1309}
1310
1311fn transmute_slice_mut<T>(bytes: &mut [u8]) -> &'_ mut [T] {
1312    // SAFETY: This is absolutely safe because `image` crate's Rgb8/Rgba8/etc. and `tbc`s Rgb8/Rgba8/etc.
1313    // have exactly the same memory layout.
1314    unsafe {
1315        std::slice::from_raw_parts_mut(
1316            bytes.as_ptr() as *mut T,
1317            bytes.len() / std::mem::size_of::<T>(),
1318        )
1319    }
1320}
1321
1322fn compress_bc1<T: tbc::color::ColorRgba8>(bytes: &[u8], width: usize, height: usize) -> Vec<u8> {
1323    tbc::encode_image_bc1_conv_u8::<T>(transmute_slice::<T>(bytes), width, height)
1324}
1325
1326fn compress_bc3<T: tbc::color::ColorRgba8>(bytes: &[u8], width: usize, height: usize) -> Vec<u8> {
1327    tbc::encode_image_bc3_conv_u8::<T>(transmute_slice::<T>(bytes), width, height)
1328}
1329
1330fn compress_r8_bc4<T: tbc::color::ColorRed8>(bytes: &[u8], width: usize, height: usize) -> Vec<u8> {
1331    tbc::encode_image_bc4_r8_conv_u8::<T>(transmute_slice::<T>(bytes), width, height)
1332}
1333
1334fn compress_rg8_bc4<T: tbc::color::ColorRedGreen8>(
1335    bytes: &[u8],
1336    width: usize,
1337    height: usize,
1338) -> Vec<u8> {
1339    tbc::encode_image_bc4_rg8_conv_u8::<T>(transmute_slice::<T>(bytes), width, height)
1340}
1341
1342fn data_hash(data: &[u8]) -> u64 {
1343    let mut hasher = FxHasher::default();
1344    data.hash(&mut hasher);
1345    hasher.finish()
1346}
1347
1348fn try_compress(
1349    pixel_kind: TexturePixelKind,
1350    bytes: &[u8],
1351    w: usize,
1352    h: usize,
1353    compression: CompressionOptions,
1354) -> Option<(Vec<u8>, TexturePixelKind)> {
1355    match (pixel_kind, compression) {
1356        (TexturePixelKind::RGB8, CompressionOptions::Speed) => Some((
1357            compress_bc1::<tbc::color::Rgb8>(bytes, w, h),
1358            TexturePixelKind::DXT1RGB,
1359        )),
1360        (TexturePixelKind::RGB8, CompressionOptions::Quality) => Some((
1361            compress_bc3::<tbc::color::Rgb8>(bytes, w, h),
1362            TexturePixelKind::DXT5RGBA,
1363        )),
1364        (TexturePixelKind::RGBA8, CompressionOptions::Speed) => Some((
1365            compress_bc1::<tbc::color::Rgba8>(bytes, w, h),
1366            TexturePixelKind::DXT1RGBA,
1367        )),
1368        (TexturePixelKind::RGBA8, CompressionOptions::Quality) => Some((
1369            compress_bc3::<tbc::color::Rgba8>(bytes, w, h),
1370            TexturePixelKind::DXT5RGBA,
1371        )),
1372        (TexturePixelKind::R8, CompressionOptions::Speed)
1373        | (TexturePixelKind::R8, CompressionOptions::Quality)
1374        | (TexturePixelKind::Luminance8, CompressionOptions::Speed)
1375        | (TexturePixelKind::Luminance8, CompressionOptions::Quality) => Some((
1376            compress_r8_bc4::<tbc::color::Red8>(bytes, w, h),
1377            TexturePixelKind::R8RGTC,
1378        )),
1379        (TexturePixelKind::RG8, CompressionOptions::Speed)
1380        | (TexturePixelKind::RG8, CompressionOptions::Quality)
1381        | (TexturePixelKind::LuminanceAlpha8, CompressionOptions::Speed)
1382        | (TexturePixelKind::LuminanceAlpha8, CompressionOptions::Quality) => Some((
1383            compress_rg8_bc4::<tbc::color::RedGreen8>(bytes, w, h),
1384            TexturePixelKind::RG8RGTC,
1385        )),
1386        _ => None,
1387    }
1388}
1389
1390fn bytes_in_mip_level(kind: TextureKind, pixel_kind: TexturePixelKind, mip: usize) -> u32 {
1391    let pixel_count = match kind {
1392        TextureKind::Line { length } => length.shr(mip),
1393        TextureKind::Rectangle { width, height } => width.shr(mip) * height.shr(mip),
1394        TextureKind::Cube { size } => 6 * size.shr(mip).pow(2),
1395        TextureKind::Volume {
1396            width,
1397            height,
1398            depth,
1399        } => width.shr(mip) * height.shr(mip) * depth.shr(mip),
1400    };
1401    match pixel_kind {
1402        // Uncompressed formats.
1403        TexturePixelKind::R8 | TexturePixelKind::Luminance8 => pixel_count,
1404        TexturePixelKind::R16
1405        | TexturePixelKind::LuminanceAlpha8
1406        | TexturePixelKind::Luminance16
1407        | TexturePixelKind::RG8
1408        | TexturePixelKind::R16F => 2 * pixel_count,
1409        TexturePixelKind::RGB8 | TexturePixelKind::SRGB8 | TexturePixelKind::BGR8 => {
1410            3 * pixel_count
1411        }
1412        TexturePixelKind::RGBA8
1413        | TexturePixelKind::SRGBA8
1414        | TexturePixelKind::BGRA8
1415        | TexturePixelKind::RG16
1416        | TexturePixelKind::LuminanceAlpha16
1417        | TexturePixelKind::R32F => 4 * pixel_count,
1418        TexturePixelKind::RGB16 | TexturePixelKind::RGB16F => 6 * pixel_count,
1419        TexturePixelKind::RGBA16 => 8 * pixel_count,
1420        TexturePixelKind::RGB32F => 12 * pixel_count,
1421        TexturePixelKind::RGBA32F => 16 * pixel_count,
1422
1423        // Compressed formats.
1424        TexturePixelKind::DXT1RGB
1425        | TexturePixelKind::DXT1RGBA
1426        | TexturePixelKind::DXT3RGBA
1427        | TexturePixelKind::DXT5RGBA
1428        | TexturePixelKind::R8RGTC
1429        | TexturePixelKind::RG8RGTC => {
1430            let block_size = match pixel_kind {
1431                TexturePixelKind::DXT1RGB
1432                | TexturePixelKind::DXT1RGBA
1433                | TexturePixelKind::R8RGTC => 8,
1434                TexturePixelKind::DXT3RGBA
1435                | TexturePixelKind::DXT5RGBA
1436                | TexturePixelKind::RG8RGTC => 16,
1437                _ => unreachable!(),
1438            };
1439            match kind {
1440                TextureKind::Line { length } => ceil_div_4(length) * block_size,
1441                TextureKind::Rectangle { width, height } => {
1442                    ceil_div_4(width) * ceil_div_4(height) * block_size
1443                }
1444                TextureKind::Cube { size } => 6 * ceil_div_4(size).pow(2) * block_size,
1445                TextureKind::Volume {
1446                    width,
1447                    height,
1448                    depth,
1449                } => ceil_div_4(width) * ceil_div_4(height) * ceil_div_4(depth) * block_size,
1450            }
1451        }
1452    }
1453}
1454
1455fn mip_byte_offset(kind: TextureKind, pixel_kind: TexturePixelKind, mut mip: usize) -> usize {
1456    // TODO: This could be done without loop.
1457    let mut offset = 0;
1458    loop {
1459        offset += bytes_in_mip_level(kind, pixel_kind, mip) as usize;
1460        mip = mip.saturating_sub(1);
1461        if mip == 0 {
1462            break;
1463        }
1464    }
1465    offset
1466}
1467
1468fn convert_pixel_type_enum(pixel_kind: TexturePixelKind) -> fr::PixelType {
1469    match pixel_kind {
1470        TexturePixelKind::R8 | TexturePixelKind::Luminance8 => fr::PixelType::U8,
1471        TexturePixelKind::RGB8 | TexturePixelKind::BGR8 => fr::PixelType::U8x3,
1472        TexturePixelKind::RGBA8 | TexturePixelKind::BGRA8 => fr::PixelType::U8x4,
1473        TexturePixelKind::RG8 | TexturePixelKind::LuminanceAlpha8 => fr::PixelType::U8x2,
1474        TexturePixelKind::R16 | TexturePixelKind::Luminance16 => fr::PixelType::U16,
1475        TexturePixelKind::RG16 | TexturePixelKind::LuminanceAlpha16 => fr::PixelType::U16x2,
1476        TexturePixelKind::RGB16 => fr::PixelType::U16x3,
1477        TexturePixelKind::RGBA16 => fr::PixelType::U16x4,
1478        TexturePixelKind::R32F => fr::PixelType::F32,
1479        _ => unreachable!(),
1480    }
1481}
1482
1483fn flip_green_channel<'a, P>(pixels: impl Iterator<Item = &'a mut P>)
1484where
1485    P: Pixel + 'a,
1486{
1487    for pixel in pixels {
1488        let green = &mut pixel.channels_mut()[1];
1489        let inverted = P::Subpixel::max_value() - *green;
1490        *green = inverted;
1491    }
1492}
1493
1494impl Texture {
1495    /// Tries to load a texture from given data in one of the following formats: PNG, BMP, TGA, JPG, DDS, GIF. Use
1496    /// this method if you want to load a texture from embedded data.
1497    ///
1498    /// # On-demand compression and mip-map generation
1499    ///
1500    /// The data can be compressed if needed to improve performance on GPU side. Mip-maps can be generated as well.
1501    /// **CAVEAT:** Compression and mip-map generation **won't** be taken into account in case of **DDS** textures,
1502    /// because DDS can already contain such data, you should generate mips and compress DDS textures manually using
1503    /// some offline tool like DirectXTexTool or similar.
1504    ///
1505    /// # Important notes
1506    ///
1507    /// Textures loaded with this method won't be correctly serialized! It means that if you'll made a scene with
1508    /// textures loaded with this method, and then save a scene, then the engine won't be able to restore the textures
1509    /// if you'll try to load the saved scene. This is essential limitation of this method, because the engine does
1510    /// not know where to get the data of the texture at loading. You should use `ResourceManager::request_texture`
1511    /// in majority of cases!
1512    ///
1513    /// # Use cases
1514    ///
1515    /// Main use cases for this method are: procedural textures, icons for GUI.
1516    pub fn load_from_memory(
1517        data: &[u8],
1518        import_options: TextureImportOptions,
1519    ) -> Result<Self, TextureError> {
1520        // DDS is special. It can contain various kinds of textures as well as textures with
1521        // various pixel formats.
1522        //
1523        // TODO: Add support for DXGI formats.
1524        if let Ok(dds) = ddsfile::Dds::read(&mut Cursor::new(data)) {
1525            let d3dformat = dds
1526                .get_d3d_format()
1527                .ok_or(TextureError::UnsupportedFormat)?;
1528            let mip_count = dds.get_num_mipmap_levels();
1529            let mut bytes = dds.data;
1530
1531            // Try to use as much formats as possible.
1532            let pixel_kind = match d3dformat {
1533                D3DFormat::DXT1 => TexturePixelKind::DXT1RGBA,
1534                D3DFormat::DXT3 => TexturePixelKind::DXT3RGBA,
1535                D3DFormat::DXT5 => TexturePixelKind::DXT5RGBA,
1536                D3DFormat::L8 | D3DFormat::A8 => TexturePixelKind::R8,
1537                D3DFormat::L16 => TexturePixelKind::R16,
1538                D3DFormat::R8G8B8 => TexturePixelKind::RGB8,
1539                D3DFormat::A8L8 => TexturePixelKind::RG8,
1540                D3DFormat::A8R8G8B8 => {
1541                    // // ARGB8 -> RGBA8
1542                    // assert_eq!(bytes.len() % 4, 0);
1543                    // for chunk in bytes.chunks_exact_mut(4) {
1544                    //     let a = chunk[0];
1545                    //     let r = chunk[1];
1546                    //     let g = chunk[2];
1547                    //     let b = chunk[3];
1548                    //     chunk[0] = r;
1549                    //     chunk[1] = g;
1550                    //     chunk[2] = b;
1551                    //     chunk[3] = a;
1552                    // }
1553                    TexturePixelKind::RGBA8
1554                }
1555                D3DFormat::G16R16 => {
1556                    // GR16 -> RG16
1557                    assert_eq!(bytes.len() % 4, 0);
1558                    for chunk in bytes.chunks_exact_mut(4) {
1559                        // Red Hi + Lo bytes
1560                        let gh = chunk[0];
1561                        let gl = chunk[1];
1562                        // Green Hi + Lo bytes
1563                        let rh = chunk[2];
1564                        let rl = chunk[3];
1565                        // Swap
1566                        chunk[0] = rh;
1567                        chunk[1] = rl;
1568                        chunk[2] = gh;
1569                        chunk[3] = gl;
1570                    }
1571                    TexturePixelKind::RG16
1572                }
1573                _ => return Err(TextureError::UnsupportedFormat),
1574            };
1575
1576            Ok(Self {
1577                pixel_kind,
1578                modifications_counter: 0,
1579                minification_filter: import_options.minification_filter,
1580                magnification_filter: import_options.magnification_filter,
1581                s_wrap_mode: import_options.s_wrap_mode,
1582                t_wrap_mode: import_options.t_wrap_mode,
1583                r_wrap_mode: import_options.r_wrap_mode,
1584                base_level: import_options.base_level,
1585                max_level: import_options.max_level,
1586                min_lod: import_options.min_lod,
1587                max_lod: import_options.max_lod,
1588                anisotropy: import_options.anisotropy,
1589                mip_count,
1590                bytes: bytes.into(),
1591                kind: if dds.header.caps2 & Caps2::CUBEMAP == Caps2::CUBEMAP {
1592                    TextureKind::Cube {
1593                        size: dds.header.width,
1594                    }
1595                } else if dds.header.caps2 & Caps2::VOLUME == Caps2::VOLUME {
1596                    TextureKind::Volume {
1597                        width: dds.header.width,
1598                        height: dds.header.height,
1599                        depth: dds.header.depth.unwrap(),
1600                    }
1601                } else {
1602                    TextureKind::Rectangle {
1603                        width: dds.header.width,
1604                        height: dds.header.height,
1605                    }
1606                },
1607                is_render_target: false,
1608                cache_index: Default::default(),
1609                lod_bias: import_options.lod_bias,
1610                sampler_properties_modifications: 1,
1611            })
1612        } else {
1613            // Commonly used formats are all rectangle textures.
1614            let mut dyn_img = image::load_from_memory(data)
1615                // Try to load as TGA, this is needed because TGA is badly designed format and does not
1616                // have an identifier in the beginning of the file (so called "magic") that allows quickly
1617                // check if the file is really contains expected data.
1618                .or_else(|_| image::load_from_memory_with_format(data, ImageFormat::Tga))?;
1619
1620            let width = dyn_img.width();
1621            let height = dyn_img.height();
1622
1623            if import_options.flip_green_channel {
1624                match dyn_img {
1625                    DynamicImage::ImageRgb8(ref mut img) => flip_green_channel(img.pixels_mut()),
1626                    DynamicImage::ImageRgba8(ref mut img) => flip_green_channel(img.pixels_mut()),
1627                    DynamicImage::ImageRgb16(ref mut img) => flip_green_channel(img.pixels_mut()),
1628                    DynamicImage::ImageRgba16(ref mut img) => flip_green_channel(img.pixels_mut()),
1629                    DynamicImage::ImageRgb32F(ref mut img) => flip_green_channel(img.pixels_mut()),
1630                    DynamicImage::ImageRgba32F(ref mut img) => flip_green_channel(img.pixels_mut()),
1631                    _ => (),
1632                }
1633            }
1634
1635            let src_pixel_kind = match dyn_img {
1636                DynamicImage::ImageLuma8(_) => TexturePixelKind::Luminance8,
1637                DynamicImage::ImageLumaA8(_) => TexturePixelKind::LuminanceAlpha8,
1638                DynamicImage::ImageRgb8(_) => TexturePixelKind::RGB8,
1639                DynamicImage::ImageRgba8(_) => TexturePixelKind::RGBA8,
1640                DynamicImage::ImageLuma16(_) => TexturePixelKind::Luminance16,
1641                DynamicImage::ImageLumaA16(_) => TexturePixelKind::LuminanceAlpha16,
1642                DynamicImage::ImageRgb16(_) => TexturePixelKind::RGB16,
1643                DynamicImage::ImageRgba16(_) => TexturePixelKind::RGBA16,
1644                DynamicImage::ImageRgb32F(_) => TexturePixelKind::RGB32F,
1645                DynamicImage::ImageRgba32F(_) => TexturePixelKind::RGBA32F,
1646                _ => return Err(TextureError::UnsupportedFormat),
1647            };
1648            let mut final_pixel_kind = src_pixel_kind;
1649
1650            let mut mip_count = 0;
1651            let mut bytes = Vec::with_capacity(
1652                width as usize * height as usize * src_pixel_kind.size_in_bytes().unwrap_or(4),
1653            );
1654
1655            if import_options.minification_filter.is_using_mip_mapping() {
1656                let src_pixel_type = convert_pixel_type_enum(src_pixel_kind);
1657                let mut level_width = width;
1658                let mut level_height = height;
1659                let mut current_level = fr::images::Image::from_vec_u8(
1660                    level_width,
1661                    level_height,
1662                    dyn_img.as_bytes().to_vec(),
1663                    src_pixel_type,
1664                )
1665                .map_err(|_| TextureError::UnsupportedFormat)?;
1666
1667                while level_width != 0 && level_height != 0 {
1668                    if mip_count != 0 {
1669                        let mut dst_img =
1670                            fr::images::Image::new(level_width, level_height, src_pixel_type);
1671
1672                        let mut resizer = fr::Resizer::new();
1673
1674                        resizer
1675                            .resize(
1676                                &current_level,
1677                                &mut dst_img,
1678                                Some(&ResizeOptions {
1679                                    algorithm: fr::ResizeAlg::Convolution(
1680                                        import_options.mip_filter.into_filter_type(),
1681                                    ),
1682                                    cropping: Default::default(),
1683                                    mul_div_alpha: true,
1684                                }),
1685                            )
1686                            .expect("Pixel types must match!");
1687
1688                        current_level = dst_img;
1689                    }
1690
1691                    mip_count += 1;
1692
1693                    if import_options.compression == CompressionOptions::NoCompression {
1694                        bytes.extend_from_slice(current_level.buffer())
1695                    } else if let Some((compressed_data, new_pixel_kind)) = try_compress(
1696                        src_pixel_kind,
1697                        current_level.buffer(),
1698                        level_width as usize,
1699                        level_height as usize,
1700                        import_options.compression,
1701                    ) {
1702                        final_pixel_kind = new_pixel_kind;
1703                        bytes.extend_from_slice(&compressed_data);
1704                    } else {
1705                        bytes.extend_from_slice(current_level.buffer())
1706                    }
1707
1708                    level_width = level_width.checked_shr(1).unwrap_or_default();
1709                    level_height = level_height.checked_shr(1).unwrap_or_default();
1710                }
1711            } else {
1712                mip_count = 1;
1713
1714                if import_options.compression == CompressionOptions::NoCompression {
1715                    bytes.extend_from_slice(dyn_img.as_bytes());
1716                } else if let Some((compressed_data, new_pixel_kind)) = try_compress(
1717                    src_pixel_kind,
1718                    dyn_img.as_bytes(),
1719                    width as usize,
1720                    height as usize,
1721                    import_options.compression,
1722                ) {
1723                    final_pixel_kind = new_pixel_kind;
1724                    bytes.extend_from_slice(&compressed_data);
1725                } else {
1726                    bytes.extend_from_slice(dyn_img.as_bytes())
1727                }
1728            }
1729
1730            Ok(Self {
1731                pixel_kind: final_pixel_kind,
1732                kind: TextureKind::Rectangle { width, height },
1733                modifications_counter: 0,
1734                bytes: bytes.into(),
1735                mip_count,
1736                minification_filter: import_options.minification_filter,
1737                magnification_filter: import_options.magnification_filter,
1738                s_wrap_mode: import_options.s_wrap_mode,
1739                t_wrap_mode: import_options.t_wrap_mode,
1740                r_wrap_mode: import_options.r_wrap_mode,
1741                base_level: import_options.base_level,
1742                max_level: import_options.max_level,
1743                min_lod: import_options.min_lod,
1744                max_lod: import_options.max_lod,
1745                anisotropy: import_options.anisotropy,
1746                is_render_target: false,
1747                cache_index: Default::default(),
1748                lod_bias: import_options.lod_bias,
1749                sampler_properties_modifications: 1,
1750            })
1751        }
1752    }
1753
1754    /// Tries to load a texture from a file.
1755    ///
1756    /// # Notes
1757    ///
1758    /// It is **not** public because you must use resource manager to load textures from external
1759    /// resources.
1760    pub(crate) async fn load_from_file<P: AsRef<Path>>(
1761        path: P,
1762        io: &dyn ResourceIo,
1763        import_options: TextureImportOptions,
1764    ) -> Result<Self, TextureError> {
1765        let data = io.load_file(path.as_ref()).await?;
1766        Self::load_from_memory(&data, import_options)
1767    }
1768
1769    /// Creates new texture instance from given parameters.
1770    ///
1771    /// # Limitations
1772    ///
1773    /// Currently textures with only one mip level are supported!
1774    pub fn from_bytes(
1775        kind: TextureKind,
1776        pixel_kind: TexturePixelKind,
1777        bytes: Vec<u8>,
1778    ) -> Option<Self> {
1779        if bytes_in_mip_level(kind, pixel_kind, 0) != bytes.len() as u32 {
1780            None
1781        } else {
1782            Some(Self {
1783                kind,
1784                modifications_counter: 0,
1785                bytes: bytes.into(),
1786                pixel_kind,
1787                ..Default::default()
1788            })
1789        }
1790    }
1791
1792    /// Sets new minification filter. It is used when texture becomes smaller.
1793    #[inline]
1794    pub fn set_minification_filter(&mut self, filter: TextureMinificationFilter) {
1795        self.minification_filter = filter;
1796        self.sampler_properties_modifications += 1;
1797    }
1798
1799    /// Returns current minification filter.
1800    #[inline]
1801    pub fn minification_filter(&self) -> TextureMinificationFilter {
1802        self.minification_filter
1803    }
1804
1805    /// Sets new magnification filter. It is used when texture is "stretching".
1806    #[inline]
1807    pub fn set_magnification_filter(&mut self, filter: TextureMagnificationFilter) {
1808        self.magnification_filter = filter;
1809        self.sampler_properties_modifications += 1;
1810    }
1811
1812    /// Returns current magnification filter.
1813    #[inline]
1814    pub fn magnification_filter(&self) -> TextureMagnificationFilter {
1815        self.magnification_filter
1816    }
1817
1818    /// Sets new S coordinate wrap mode.
1819    #[inline]
1820    pub fn set_s_wrap_mode(&mut self, s_wrap_mode: TextureWrapMode) {
1821        self.s_wrap_mode = s_wrap_mode;
1822        self.sampler_properties_modifications += 1;
1823    }
1824
1825    /// Returns current S coordinate wrap mode.
1826    #[inline]
1827    pub fn s_wrap_mode(&self) -> TextureWrapMode {
1828        self.s_wrap_mode
1829    }
1830
1831    /// Sets new T coordinate wrap mode.
1832    #[inline]
1833    pub fn set_t_wrap_mode(&mut self, t_wrap_mode: TextureWrapMode) {
1834        self.t_wrap_mode = t_wrap_mode;
1835        self.sampler_properties_modifications += 1;
1836    }
1837
1838    /// Returns current T coordinate wrap mode.
1839    #[inline]
1840    pub fn t_wrap_mode(&self) -> TextureWrapMode {
1841        self.t_wrap_mode
1842    }
1843
1844    /// Sets new R coordinate wrap mode.
1845    #[inline]
1846    pub fn set_r_wrap_mode(&mut self, r_wrap_mode: TextureWrapMode) {
1847        self.r_wrap_mode = r_wrap_mode;
1848        self.sampler_properties_modifications += 1;
1849    }
1850
1851    pub fn sampler_modifications_count(&self) -> u64 {
1852        self.sampler_properties_modifications
1853    }
1854
1855    /// Returns current T coordinate wrap mode.
1856    #[inline]
1857    pub fn r_wrap_mode(&self) -> TextureWrapMode {
1858        self.r_wrap_mode
1859    }
1860
1861    /// Returns total mip count.
1862    #[inline]
1863    pub fn mip_count(&self) -> u32 {
1864        self.mip_count
1865    }
1866
1867    /// Returns texture kind.
1868    #[inline]
1869    pub fn kind(&self) -> TextureKind {
1870        self.kind
1871    }
1872
1873    /// Returns total amount of modifications done on the texture.
1874    #[inline]
1875    pub fn modifications_count(&self) -> u64 {
1876        self.modifications_counter
1877    }
1878
1879    /// Calculates current data hash.
1880    #[inline]
1881    pub fn calculate_data_hash(&self) -> u64 {
1882        data_hash(&self.bytes.0)
1883    }
1884
1885    /// Returns current pixel kind.
1886    #[inline]
1887    pub fn pixel_kind(&self) -> TexturePixelKind {
1888        self.pixel_kind
1889    }
1890
1891    /// Returns the index of the lowest defined mipmap level.
1892    #[inline]
1893    pub fn base_level(&self) -> usize {
1894        self.base_level
1895    }
1896
1897    /// Specifies the index of the lowest defined mipmap level. Keep in mind, that the texture data
1898    /// should provide the actual mip map level defined by the provided value, otherwise the
1899    /// rendering will be incorrect (probably just black on majority of implementations) and glitchy.
1900    #[inline]
1901    pub fn set_base_level(&mut self, base_level: usize) {
1902        self.base_level = base_level;
1903    }
1904
1905    /// Returns the index of the highest defined mipmap level.
1906    #[inline]
1907    pub fn max_level(&self) -> usize {
1908        self.max_level
1909    }
1910
1911    /// Sets the index of the highest defined mipmap level. Keep in mind, that the texture data
1912    /// should provide the actual mip map level defined by the provided value, otherwise the
1913    /// rendering will be incorrect (probably just black on majority of implementations) and glitchy.
1914    #[inline]
1915    pub fn set_max_level(&mut self, max_level: usize) {
1916        self.max_level = max_level;
1917    }
1918
1919    /// Returns the minimum level-of-detail parameter. See [`Self::set_min_lod`] for more info.
1920    #[inline]
1921    pub fn min_lod(&self) -> f32 {
1922        self.min_lod
1923    }
1924
1925    /// Sets the minimum level-of-detail parameter. This floating-point value limits the selection
1926    /// of highest resolution mipmap (lowest mipmap level). The initial value is -1000.0.
1927    #[inline]
1928    pub fn set_min_lod(&mut self, min_lod: f32) {
1929        self.sampler_properties_modifications += 1;
1930        self.min_lod = min_lod;
1931    }
1932
1933    /// Returns the maximum level-of-detail parameter. See [`Self::set_max_lod`] for more info.
1934    #[inline]
1935    pub fn max_lod(&self) -> f32 {
1936        self.max_lod
1937    }
1938
1939    /// Sets the maximum level-of-detail parameter. This floating-point value limits the selection
1940    /// of the lowest resolution mipmap (highest mipmap level). The initial value is 1000.
1941    #[inline]
1942    pub fn set_max_lod(&mut self, max_lod: f32) {
1943        self.sampler_properties_modifications += 1;
1944        self.max_lod = max_lod;
1945    }
1946
1947    /// Returns a fixed bias value that is to be added to the level-of-detail parameter for the
1948    /// texture before texture sampling. See [`Self::set_lod_bias`] for more info.
1949    #[inline]
1950    pub fn lod_bias(&self) -> f32 {
1951        self.lod_bias
1952    }
1953
1954    /// Specifies a fixed bias value that is to be added to the level-of-detail parameter for the
1955    /// texture before texture sampling. The specified value is added to the shader-supplied bias
1956    /// value (if any) and subsequently clamped into the implementation-defined range
1957    /// `−bias_max..bias_max`, where `bias_max` is the value that can be fetched from the current
1958    /// graphics server. The initial value is 0.0.
1959    #[inline]
1960    pub fn set_lod_bias(&mut self, lod_bias: f32) {
1961        self.sampler_properties_modifications += 1;
1962        self.lod_bias = lod_bias;
1963    }
1964
1965    /// Returns current data as immutable slice.
1966    pub fn data(&self) -> &[u8] {
1967        &self.bytes
1968    }
1969
1970    /// Tries to cast the internal data buffer to the given type. Type casting will succeed only if the the
1971    /// size of the type `T` is equal with the size of the pixel.
1972    ///
1973    /// ## Important notes
1974    ///
1975    /// While this function is safe, there's no guarantee that the actual type-casted data will match the layout
1976    /// of your data structure. For example, you could have a pixel of type `RG16`, where each pixel consumes 2
1977    /// bytes (4 in total) and cast it to the structure `struct Rgba8 { r: u8, g: u8, b: u8, a: u8 }` which is
1978    /// safe in terms of memory access (both are 4 bytes total), but not ok in terms of actual data. Be careful
1979    /// when using this method.
1980    ///
1981    /// Keep in mind, that this method will return a reference to **all** data in the texture, including all mip
1982    /// levels. If you need to get typed data from specific mip level use [`Self::mip_level_data_of_type`].
1983    pub fn data_of_type<T: Sized>(&self) -> Option<&[T]> {
1984        if let Some(pixel_size) = self.pixel_kind.size_in_bytes() {
1985            if pixel_size == std::mem::size_of::<T>() {
1986                return Some(transmute_slice(&self.bytes));
1987            }
1988        }
1989        None
1990    }
1991
1992    /// Returns data of the given mip level.
1993    #[inline]
1994    pub fn mip_level_data(&self, mip: usize) -> &[u8] {
1995        let mip_begin = mip
1996            .checked_sub(1)
1997            .map(|prev| mip_byte_offset(self.kind, self.pixel_kind, prev))
1998            .unwrap_or_default();
1999        let mip_end = mip_byte_offset(self.kind, self.pixel_kind, mip);
2000        &self.bytes[mip_begin..mip_end]
2001    }
2002
2003    /// Tries to cast the specific mip level data of the internal data buffer to the given type. Type casting
2004    /// will succeed only if the the size of the type `T` is equal with the size of the pixel.
2005    ///
2006    /// ## Important notes
2007    ///
2008    /// While this function is safe, there's no guarantee that the actual type-casted data will match the layout
2009    /// of your data structure. For example, you could have a pixel of type `RG16`, where each pixel consumes 2
2010    /// bytes (4 in total) and cast it to the structure `struct Rgba8 { r: u8, g: u8, b: u8, a: u8 }` which is
2011    /// safe in terms of memory access (both are 4 bytes total), but not ok in terms of actual data. Be careful
2012    /// when using this method.
2013    pub fn mip_level_data_of_type<T: Sized>(&self, mip: usize) -> Option<&[T]> {
2014        if let Some(pixel_size) = self.pixel_kind.size_in_bytes() {
2015            if pixel_size == std::mem::size_of::<T>() {
2016                return Some(transmute_slice(self.mip_level_data(mip)));
2017            }
2018        }
2019        None
2020    }
2021
2022    /// Returns true if the texture is used as render target.
2023    #[inline]
2024    pub fn is_render_target(&self) -> bool {
2025        self.is_render_target
2026    }
2027
2028    /// Max samples for anisotropic filtering. Default value is 16.0 (max).
2029    /// However real value passed to GPU will be clamped to maximum supported
2030    /// by current GPU. To disable anisotropic filtering set this to 1.0.
2031    /// Typical values are 2.0, 4.0, 8.0, 16.0.
2032    #[inline]
2033    pub fn set_anisotropy_level(&mut self, anisotropy: f32) {
2034        self.sampler_properties_modifications += 1;
2035        self.anisotropy = anisotropy.max(1.0);
2036    }
2037
2038    /// Returns current anisotropy level.
2039    #[inline]
2040    pub fn anisotropy_level(&self) -> f32 {
2041        self.anisotropy
2042    }
2043
2044    /// Returns a special reference holder that provides mutable access to content of the
2045    /// texture and automatically calculates hash of the data in its destructor.
2046    #[inline]
2047    pub fn modify(&mut self) -> TextureDataRefMut<'_> {
2048        TextureDataRefMut { texture: self }
2049    }
2050}
2051
2052/// A special reference holder that provides mutable access to content of the
2053/// texture and automatically calculates hash of the data in its destructor.
2054pub struct TextureDataRefMut<'a> {
2055    texture: &'a mut Texture,
2056}
2057
2058impl Drop for TextureDataRefMut<'_> {
2059    fn drop(&mut self) {
2060        self.texture.modifications_counter += 1;
2061    }
2062}
2063
2064impl Deref for TextureDataRefMut<'_> {
2065    type Target = Texture;
2066
2067    fn deref(&self) -> &Self::Target {
2068        self.texture
2069    }
2070}
2071
2072impl DerefMut for TextureDataRefMut<'_> {
2073    fn deref_mut(&mut self) -> &mut Self::Target {
2074        self.texture
2075    }
2076}
2077
2078impl TextureDataRefMut<'_> {
2079    /// Returns mutable reference to the data of the texture.
2080    pub fn data_mut(&mut self) -> &mut [u8] {
2081        &mut self.texture.bytes
2082    }
2083
2084    /// Tries to cast the internal data buffer to the given type. Type casting will succeed only if the the
2085    /// size of the type `T` is equal with the size of the pixel. **WARNING:** While this function is safe, there's
2086    /// no guarantee that the actual type-casted data will match the layout of your data structure. For example,
2087    /// you could have a pixel of type `RG16`, where each pixel consumes 2 bytes (4 in total) and cast it to the
2088    /// structure `struct Rgba8 { r: u8, g: u8, b: u8, a: u8 }` which is safe in terms of memory access (both are 4
2089    /// bytes total), but not ok in terms of actual data. Be careful when using the method.
2090    pub fn data_mut_of_type<T: Sized>(&mut self) -> Option<&mut [T]> {
2091        if let Some(pixel_size) = self.texture.pixel_kind.size_in_bytes() {
2092            if pixel_size == std::mem::size_of::<T>() {
2093                return Some(transmute_slice_mut(&mut self.texture.bytes));
2094            }
2095        }
2096        None
2097    }
2098}