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