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