rg3d/resource/
texture.rs

1//! Texture is an image that used to fill faces to add details to them.
2//!
3//! In most cases textures are just 2D images, however there are some exclusions to that -
4//! for example cube maps, that may be used for environment mapping. rg3d supports 1D, 2D,
5//! 3D and Cube textures.
6//!
7//! ## Supported formats
8//!
9//! To load images and decode them, rg3d uses image and ddsfile crates. Here is the list of
10//! supported formats: png, tga, bmp, dds, jpg, gif, tiff, dds.
11//!
12//! ## Compressed textures
13//!
14//! rg3d supports most commonly used formats of compressed textures: DXT1, DXT3, DXT5.
15//!
16//! ## Render target
17//!
18//! Texture can be used as render target to render scene in it. To do this you should use
19//! new_render_target method and pass its result to scene's render target property. Renderer
20//! will automatically provide you info about metrics of texture, but it won't give you
21//! access to pixels of render target.
22
23use crate::{
24    asset::{define_new_resource, Resource, ResourceData, ResourceState},
25    core::{
26        futures::io::Error,
27        inspect::{Inspect, PropertyInfo},
28        io::{self, FileLoadError},
29        visitor::{PodVecView, Visit, VisitError, VisitResult, Visitor},
30    },
31    engine::resource_manager::ImportOptions,
32};
33use ddsfile::{Caps2, D3DFormat};
34use fxhash::FxHasher;
35use image::{
36    imageops::FilterType, ColorType, DynamicImage, GenericImageView, ImageError, ImageFormat,
37};
38use serde::{Deserialize, Serialize};
39use std::{
40    borrow::Cow,
41    fmt::{Debug, Formatter},
42    hash::{Hash, Hasher},
43    io::Cursor,
44    ops::{Deref, DerefMut},
45    path::{Path, PathBuf},
46};
47use strum_macros::{AsRefStr, EnumString, EnumVariantNames};
48
49/// Texture kind.
50#[derive(Copy, Clone, Debug)]
51pub enum TextureKind {
52    /// 1D texture.
53    Line {
54        /// Length of the texture.
55        length: u32,
56    },
57    /// 2D texture.
58    Rectangle {
59        /// Width of the texture.
60        width: u32,
61        /// Height of the texture.
62        height: u32,
63    },
64    /// Cube texture.
65    Cube {
66        /// Width of the cube face.
67        width: u32,
68        /// Height of the cube face.
69        height: u32,
70    },
71    /// Volume texture (3D).
72    Volume {
73        /// Width of the volume.
74        width: u32,
75        /// Height of the volume.
76        height: u32,
77        /// Depth of the volume.
78        depth: u32,
79    },
80}
81
82impl Default for TextureKind {
83    fn default() -> Self {
84        Self::Rectangle {
85            width: 0,
86            height: 0,
87        }
88    }
89}
90
91impl Visit for TextureKind {
92    fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
93        visitor.enter_region(name)?;
94
95        let mut id = match self {
96            TextureKind::Line { .. } => 0,
97            TextureKind::Rectangle { .. } => 1,
98            TextureKind::Cube { .. } => 2,
99            TextureKind::Volume { .. } => 3,
100        };
101        id.visit("Id", visitor)?;
102        if visitor.is_reading() {
103            *self = match id {
104                0 => TextureKind::Line { length: 0 },
105                1 => TextureKind::Rectangle {
106                    width: 0,
107                    height: 0,
108                },
109                2 => TextureKind::Cube {
110                    width: 0,
111                    height: 0,
112                },
113                3 => TextureKind::Volume {
114                    width: 0,
115                    height: 0,
116                    depth: 0,
117                },
118                _ => {
119                    return VisitResult::Err(VisitError::User(format!(
120                        "Invalid texture kind {}!",
121                        id
122                    )))
123                }
124            };
125        }
126        match self {
127            TextureKind::Line { length } => {
128                length.visit("Length", visitor)?;
129            }
130            TextureKind::Rectangle { width, height } => {
131                width.visit("Width", visitor)?;
132                height.visit("Height", visitor)?;
133            }
134            TextureKind::Cube { width, height } => {
135                width.visit("Width", visitor)?;
136                height.visit("Height", visitor)?;
137            }
138            TextureKind::Volume {
139                width,
140                height,
141                depth,
142            } => {
143                width.visit("Width", visitor)?;
144                height.visit("Height", visitor)?;
145                depth.visit("Depth", visitor)?;
146            }
147        }
148
149        visitor.leave_region()
150    }
151}
152
153#[derive(Default, Clone)]
154struct TextureBytes(Vec<u8>);
155
156impl Visit for TextureBytes {
157    fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
158        self.0.visit(name, visitor)
159    }
160}
161
162impl Debug for TextureBytes {
163    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
164        write!(f, "Texture has {} bytes", self.0.len())
165    }
166}
167
168impl From<Vec<u8>> for TextureBytes {
169    fn from(bytes: Vec<u8>) -> Self {
170        Self(bytes)
171    }
172}
173
174impl Deref for TextureBytes {
175    type Target = Vec<u8>;
176
177    fn deref(&self) -> &Self::Target {
178        &self.0
179    }
180}
181
182impl DerefMut for TextureBytes {
183    fn deref_mut(&mut self) -> &mut Self::Target {
184        &mut self.0
185    }
186}
187
188/// Actual texture data.
189#[derive(Debug)]
190pub struct TextureData {
191    path: PathBuf,
192    kind: TextureKind,
193    bytes: TextureBytes,
194    pixel_kind: TexturePixelKind,
195    minification_filter: TextureMinificationFilter,
196    magnification_filter: TextureMagnificationFilter,
197    s_wrap_mode: TextureWrapMode,
198    t_wrap_mode: TextureWrapMode,
199    mip_count: u32,
200    anisotropy: f32,
201    serialize_content: bool,
202    data_hash: u64,
203    is_render_target: bool,
204}
205
206impl ResourceData for TextureData {
207    fn path(&self) -> Cow<Path> {
208        Cow::Borrowed(&self.path)
209    }
210
211    fn set_path(&mut self, path: PathBuf) {
212        self.path = path;
213    }
214}
215
216impl Visit for TextureData {
217    fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
218        visitor.enter_region(name)?;
219
220        let mut kind = self.pixel_kind.id();
221        kind.visit("KindId", visitor)?;
222        if visitor.is_reading() {
223            self.pixel_kind = TexturePixelKind::new(kind)?;
224        }
225
226        self.path.visit("Path", visitor)?;
227
228        self.minification_filter
229            .visit("MinificationFilter", visitor)?;
230        self.magnification_filter
231            .visit("MagnificationFilter", visitor)?;
232        self.anisotropy.visit("Anisotropy", visitor)?;
233        self.s_wrap_mode.visit("SWrapMode", visitor)?;
234        self.t_wrap_mode.visit("TWrapMode", visitor)?;
235        self.mip_count.visit("MipCount", visitor)?;
236        self.kind.visit("Kind", visitor)?;
237        let _ = self.serialize_content.visit("SerializeContent", visitor);
238
239        if self.serialize_content {
240            let mut bytes_view = PodVecView::from_pod_vec(&mut self.bytes);
241            bytes_view.visit("Data", visitor)?;
242        }
243
244        visitor.leave_region()
245    }
246}
247
248impl Default for TextureData {
249    /// It is very important to mention that defaults may be different for texture when you
250    /// importing them through resource manager, see
251    /// [TextureImportOptions](../engine/resource_manager/struct.TextureImportOptions.html) for more info.
252    fn default() -> Self {
253        Self {
254            path: PathBuf::new(),
255            kind: TextureKind::Rectangle {
256                width: 0,
257                height: 0,
258            },
259            bytes: Default::default(),
260            pixel_kind: TexturePixelKind::RGBA8,
261            minification_filter: TextureMinificationFilter::LinearMipMapLinear,
262            magnification_filter: TextureMagnificationFilter::Linear,
263            s_wrap_mode: TextureWrapMode::Repeat,
264            t_wrap_mode: TextureWrapMode::Repeat,
265            mip_count: 1,
266            anisotropy: 16.0,
267            serialize_content: false,
268            data_hash: 0,
269            is_render_target: false,
270        }
271    }
272}
273
274/// Allows you to define a set of parameters for a texture resource.
275///
276/// # Details
277///
278/// Usually the content of this structure is stored in a separate file with .options extension. Typical content of
279/// a settings file should look like this:
280///
281/// ```text
282/// (
283///     minification_filter: Linear,
284///     magnification_filter: Linear,
285///     s_wrap_mode: Repeat,
286///     t_wrap_mode: ClampToEdge,
287///     anisotropy: 8.0,
288///     compression: NoCompression,    
289/// )
290/// ```
291#[derive(Clone, Deserialize, Serialize, Inspect)]
292pub struct TextureImportOptions {
293    #[serde(default)]
294    pub(crate) minification_filter: TextureMinificationFilter,
295    #[serde(default)]
296    pub(crate) magnification_filter: TextureMagnificationFilter,
297    #[serde(default)]
298    pub(crate) s_wrap_mode: TextureWrapMode,
299    #[serde(default)]
300    pub(crate) t_wrap_mode: TextureWrapMode,
301    #[serde(default)]
302    pub(crate) anisotropy: f32,
303    #[serde(default)]
304    pub(crate) compression: CompressionOptions,
305}
306
307impl Default for TextureImportOptions {
308    fn default() -> Self {
309        Self {
310            minification_filter: TextureMinificationFilter::LinearMipMapLinear,
311            magnification_filter: TextureMagnificationFilter::Linear,
312            s_wrap_mode: TextureWrapMode::Repeat,
313            t_wrap_mode: TextureWrapMode::Repeat,
314            anisotropy: 16.0,
315            compression: CompressionOptions::Quality,
316        }
317    }
318}
319
320impl ImportOptions for TextureImportOptions {}
321
322impl TextureImportOptions {
323    /// Sets new minification filter which will be applied to every imported texture as
324    /// default value.
325    pub fn with_minification_filter(
326        mut self,
327        minification_filter: TextureMinificationFilter,
328    ) -> Self {
329        self.minification_filter = minification_filter;
330        self
331    }
332
333    /// Sets new minification filter which will be applied to every imported texture as
334    /// default value.
335    pub fn set_minification_filter(&mut self, minification_filter: TextureMinificationFilter) {
336        self.minification_filter = minification_filter;
337    }
338
339    /// Sets new magnification filter which will be applied to every imported texture as
340    /// default value.
341    pub fn with_magnification_filter(
342        mut self,
343        magnification_filter: TextureMagnificationFilter,
344    ) -> Self {
345        self.magnification_filter = magnification_filter;
346        self
347    }
348
349    /// Sets new magnification filter which will be applied to every imported texture as
350    /// default value.
351    pub fn set_magnification_filter(&mut self, magnification_filter: TextureMagnificationFilter) {
352        self.magnification_filter = magnification_filter;
353    }
354
355    /// Sets new S coordinate wrap mode which will be applied to every imported texture as
356    /// default value.
357    pub fn with_s_wrap_mode(mut self, s_wrap_mode: TextureWrapMode) -> Self {
358        self.s_wrap_mode = s_wrap_mode;
359        self
360    }
361
362    /// Sets new S coordinate wrap mode which will be applied to every imported texture as
363    /// default value.
364    pub fn set_s_wrap_mode(&mut self, s_wrap_mode: TextureWrapMode) {
365        self.s_wrap_mode = s_wrap_mode;
366    }
367
368    /// Sets new T coordinate wrap mode which will be applied to every imported texture as
369    /// default value.
370    pub fn with_t_wrap_mode(mut self, t_wrap_mode: TextureWrapMode) -> Self {
371        self.t_wrap_mode = t_wrap_mode;
372        self
373    }
374
375    /// Sets new T coordinate wrap mode which will be applied to every imported texture as
376    /// default value.
377    pub fn set_t_wrap_mode(&mut self, t_wrap_mode: TextureWrapMode) {
378        self.t_wrap_mode = t_wrap_mode;
379    }
380
381    /// Sets new anisotropy level which will be applied to every imported texture as
382    /// default value.
383    pub fn with_anisotropy(mut self, anisotropy: f32) -> Self {
384        self.anisotropy = anisotropy.min(1.0);
385        self
386    }
387
388    /// Sets new anisotropy level which will be applied to every imported texture as
389    /// default value.
390    pub fn set_anisotropy(&mut self, anisotropy: f32) {
391        self.anisotropy = anisotropy.min(1.0);
392    }
393
394    /// Sets desired texture compression.
395    pub fn with_compression(mut self, compression: CompressionOptions) -> Self {
396        self.compression = compression;
397        self
398    }
399
400    /// Sets desired texture compression.
401    pub fn set_compression(&mut self, compression: CompressionOptions) {
402        self.compression = compression;
403    }
404}
405
406define_new_resource!(
407    /// See module docs.
408    Texture<TextureData, TextureError>
409);
410
411/// Texture state alias.
412pub type TextureState = ResourceState<TextureData, TextureError>;
413
414impl Texture {
415    /// Creates new render target for a scene. This method automatically configures GPU texture
416    /// to correct settings, after render target was created, it must not be modified, otherwise
417    /// result is undefined.
418    pub fn new_render_target(width: u32, height: u32) -> Self {
419        Self(Resource::new(TextureState::Ok(TextureData {
420            path: Default::default(),
421            // Render target will automatically set width and height before rendering.
422            kind: TextureKind::Rectangle { width, height },
423            bytes: Default::default(),
424            pixel_kind: TexturePixelKind::RGBA8,
425            minification_filter: TextureMinificationFilter::Linear,
426            magnification_filter: TextureMagnificationFilter::Linear,
427            s_wrap_mode: TextureWrapMode::Repeat,
428            t_wrap_mode: TextureWrapMode::Repeat,
429            mip_count: 1,
430            anisotropy: 1.0,
431            serialize_content: false,
432            data_hash: 0,
433            is_render_target: true,
434        })))
435    }
436
437    /// Tries to load a texture from given data. Use this method if you want to
438    /// load a texture from embedded data.
439    ///
440    /// # On-demand compression
441    ///
442    /// The data can be compressed if needed to improve performance on GPU side.
443    ///
444    /// # Important notes
445    ///
446    /// Textures loaded with this method won't be correctly serialized! It means
447    /// that if you'll made a scene with textures loaded with this method, and then
448    /// save a scene, then the engine won't be able to restore the textures if you'll
449    /// try to load the saved scene. This is essential limitation of this method,
450    /// because the engine does not know where to get the data of the texture at
451    /// loading. You should use `ResourceManager::request_texture` in majority of cases!
452    ///
453    /// Main use cases for this method are: procedural textures, icons for GUI.
454    pub fn load_from_memory(
455        data: &[u8],
456        compression: CompressionOptions,
457        gen_mip_maps: bool,
458    ) -> Result<Self, TextureError> {
459        Ok(Self(Resource::new(TextureState::Ok(
460            TextureData::load_from_memory(data, compression, gen_mip_maps)?,
461        ))))
462    }
463
464    /// Tries to create new texture from given parameters, it may fail only if size of data passed
465    /// in does not match with required.
466    pub fn from_bytes(
467        kind: TextureKind,
468        pixel_kind: TexturePixelKind,
469        bytes: Vec<u8>,
470        serialize_content: bool,
471    ) -> Option<Self> {
472        Some(Self(Resource::new(TextureState::Ok(
473            TextureData::from_bytes(kind, pixel_kind, bytes, serialize_content)?,
474        ))))
475    }
476}
477
478/// The texture magnification function is used when the pixel being textured maps to an area
479/// less than or equal to one texture element.
480#[derive(
481    Copy,
482    Clone,
483    Debug,
484    Hash,
485    PartialOrd,
486    PartialEq,
487    Deserialize,
488    Serialize,
489    Inspect,
490    EnumVariantNames,
491    EnumString,
492    AsRefStr,
493)]
494#[repr(u32)]
495pub enum TextureMagnificationFilter {
496    /// Returns the value of the texture element that is nearest to the center of the pixel
497    /// being textured.
498    Nearest = 0,
499
500    /// Returns the weighted average of the four texture elements that are closest to the
501    /// center of the pixel being textured.
502    Linear = 1,
503}
504
505impl Default for TextureMagnificationFilter {
506    fn default() -> Self {
507        Self::Linear
508    }
509}
510
511impl Visit for TextureMagnificationFilter {
512    fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
513        visitor.enter_region(name)?;
514
515        let mut id = *self as u32;
516        id.visit("Id", visitor)?;
517
518        if visitor.is_reading() {
519            *self = match id {
520                0 => TextureMagnificationFilter::Nearest,
521                1 => TextureMagnificationFilter::Linear,
522                _ => {
523                    return VisitResult::Err(VisitError::User(format!(
524                        "Invalid magnification filter {}!",
525                        id
526                    )))
527                }
528            }
529        }
530
531        visitor.leave_region()
532    }
533}
534
535/// The texture minifying function is used whenever the pixel being textured maps to an area
536/// greater than one texture element.
537#[derive(
538    Copy,
539    Clone,
540    Debug,
541    Hash,
542    PartialOrd,
543    PartialEq,
544    Deserialize,
545    Serialize,
546    Inspect,
547    EnumVariantNames,
548    EnumString,
549    AsRefStr,
550)]
551#[repr(u32)]
552pub enum TextureMinificationFilter {
553    /// Returns the value of the texture element that is nearest to the center of the pixel
554    /// being textured.
555    Nearest = 0,
556
557    /// Chooses the mipmap that most closely matches the size of the pixel being textured and
558    /// uses the Nearest criterion (the texture element nearest to the center of the pixel)
559    /// to produce a texture value.
560    NearestMipMapNearest = 1,
561
562    /// Chooses the two mipmaps that most closely match the size of the pixel being textured
563    /// and uses the Nearest criterion (the texture element nearest to the center of the pixel)
564    /// to produce a texture value from each mipmap. The final texture value is a weighted average
565    /// of those two values.
566    NearestMipMapLinear = 2,
567
568    /// Returns the weighted average of the four texture elements that are closest to the
569    /// center of the pixel being textured.
570    Linear = 3,
571
572    /// Chooses the mipmap that most closely matches the size of the pixel being textured and
573    /// uses the Linear criterion (a weighted average of the four texture elements that are
574    /// closest to the center of the pixel) to produce a texture value.
575    LinearMipMapNearest = 4,
576
577    /// Chooses the two mipmaps that most closely match the size of the pixel being textured
578    /// and uses the Linear criterion (a weighted average of the four texture elements that
579    /// are closest to the center of the pixel) to produce a texture value from each mipmap.
580    /// The final texture value is a weighted average of those two values.
581    LinearMipMapLinear = 5,
582}
583
584impl TextureMinificationFilter {
585    /// Returns true if minification filter is using mip mapping, false - otherwise.
586    pub fn is_using_mip_mapping(self) -> bool {
587        match self {
588            TextureMinificationFilter::Nearest | TextureMinificationFilter::Linear => false,
589            TextureMinificationFilter::NearestMipMapNearest
590            | TextureMinificationFilter::LinearMipMapLinear
591            | TextureMinificationFilter::NearestMipMapLinear
592            | TextureMinificationFilter::LinearMipMapNearest => true,
593        }
594    }
595}
596
597impl Default for TextureMinificationFilter {
598    fn default() -> Self {
599        Self::LinearMipMapLinear
600    }
601}
602
603impl Visit for TextureMinificationFilter {
604    fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
605        visitor.enter_region(name)?;
606
607        let mut id = *self as u32;
608        id.visit("Id", visitor)?;
609
610        if visitor.is_reading() {
611            *self = match id {
612                0 => TextureMinificationFilter::Nearest,
613                1 => TextureMinificationFilter::NearestMipMapNearest,
614                2 => TextureMinificationFilter::NearestMipMapLinear,
615                3 => TextureMinificationFilter::Linear,
616                4 => TextureMinificationFilter::LinearMipMapNearest,
617                5 => TextureMinificationFilter::LinearMipMapLinear,
618                _ => {
619                    return VisitResult::Err(VisitError::User(format!(
620                        "Invalid minification filter {}!",
621                        id
622                    )))
623                }
624            }
625        }
626
627        visitor.leave_region()
628    }
629}
630
631/// Defines a law of texture coordinate modification.
632#[derive(
633    Copy,
634    Clone,
635    Debug,
636    Hash,
637    PartialOrd,
638    PartialEq,
639    Deserialize,
640    Serialize,
641    Inspect,
642    EnumVariantNames,
643    EnumString,
644    AsRefStr,
645)]
646#[repr(u32)]
647pub enum TextureWrapMode {
648    /// Causes the integer part of a coordinate to be ignored; GPU uses only the fractional part,
649    /// thereby creating a repeating pattern.
650    Repeat = 0,
651
652    /// Causes a coordinates to be clamped to the range range, where N is the size of the texture
653    /// in the direction of clamping
654    ClampToEdge = 1,
655
656    /// Evaluates a coordinates in a similar manner to ClampToEdge. However, in cases where clamping
657    /// would have occurred in ClampToEdge mode, the fetched texel data is substituted with the values
658    /// specified by border color.
659    ClampToBorder = 2,
660
661    /// Causes the a coordinate to be set to the fractional part of the texture coordinate if the integer
662    /// part of coordinate is even; if the integer part of coordinate is odd, then the coordinate texture
663    /// coordinate is set to 1-frac, where frac represents the fractional part of coordinate.
664    MirroredRepeat = 3,
665
666    /// Causes a coordinate to be repeated as for MirroredRepeat for one repetition of the texture, at
667    /// which point the coordinate to be clamped as in ClampToEdge.
668    MirrorClampToEdge = 4,
669}
670
671impl Default for TextureWrapMode {
672    fn default() -> Self {
673        Self::Repeat
674    }
675}
676
677impl Visit for TextureWrapMode {
678    fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
679        visitor.enter_region(name)?;
680
681        let mut id = *self as u32;
682        id.visit("Id", visitor)?;
683
684        if visitor.is_reading() {
685            *self = match id {
686                0 => TextureWrapMode::Repeat,
687                1 => TextureWrapMode::ClampToEdge,
688                2 => TextureWrapMode::ClampToBorder,
689                3 => TextureWrapMode::MirroredRepeat,
690                4 => TextureWrapMode::MirrorClampToEdge,
691                _ => {
692                    return VisitResult::Err(VisitError::User(format!("Invalid wrap mode {}!", id)))
693                }
694            }
695        }
696
697        visitor.leave_region()
698    }
699}
700
701/// Texture kind defines pixel format of texture.
702#[derive(Copy, Clone, PartialEq, Eq, Debug)]
703#[repr(u32)]
704pub enum TexturePixelKind {
705    /// 1 byte red.
706    R8 = 0,
707
708    /// Red, green, and blue components, each by 1 byte.
709    RGB8 = 1,
710
711    /// Red, green, blue, and alpha components, each by 1 byte.
712    RGBA8 = 2,
713
714    /// Red and green, each by 1 byte.
715    RG8 = 3,
716
717    /// 2 byte red.
718    R16 = 4,
719
720    /// Red and green, each by 2 byte.
721    RG16 = 5,
722
723    /// Blue, green, and red components, each by 1 byte.
724    BGR8 = 6,
725
726    /// Blue, green, red and alpha components, each by 1 byte.
727    BGRA8 = 7,
728
729    /// Red, green, and blue components, each by 2 byte.
730    RGB16 = 8,
731
732    /// Red, green, blue, and alpha components, each by 2 byte.
733    RGBA16 = 9,
734
735    /// Compressed S3TC DXT1 RGB (no alpha).
736    DXT1RGB = 10,
737
738    /// Compressed S3TC DXT1 RGBA.
739    DXT1RGBA = 11,
740
741    /// Compressed S3TC DXT3 RGBA.
742    DXT3RGBA = 12,
743
744    /// Compressed S3TC DXT5 RGBA.
745    DXT5RGBA = 13,
746
747    /// Compressed R8 texture (RGTC).
748    R8RGTC = 14,
749
750    /// Compressed RG8 texture (RGTC).
751    RG8RGTC = 15,
752}
753
754impl TexturePixelKind {
755    fn new(id: u32) -> Result<Self, String> {
756        match id {
757            0 => Ok(Self::R8),
758            1 => Ok(Self::RGB8),
759            2 => Ok(Self::RGBA8),
760            3 => Ok(Self::RG8),
761            4 => Ok(Self::R16),
762            5 => Ok(Self::RG16),
763            6 => Ok(Self::BGR8),
764            7 => Ok(Self::BGRA8),
765            8 => Ok(Self::RGB16),
766            9 => Ok(Self::RGBA16),
767            10 => Ok(Self::DXT1RGB),
768            11 => Ok(Self::DXT1RGBA),
769            12 => Ok(Self::DXT3RGBA),
770            13 => Ok(Self::DXT5RGBA),
771            14 => Ok(Self::R8RGTC),
772            15 => Ok(Self::RG8RGTC),
773            _ => Err(format!("Invalid texture kind {}!", id)),
774        }
775    }
776
777    fn id(self) -> u32 {
778        self as u32
779    }
780}
781
782/// An error that may occur during texture operations.
783#[derive(Debug, thiserror::Error)]
784pub enum TextureError {
785    /// Format (pixel format, dimensions) is not supported.
786    #[error("Unsupported format!")]
787    UnsupportedFormat,
788    /// An io error.
789    #[error("An i/o error has occurred: {0}")]
790    Io(std::io::Error),
791    /// Internal image crate error.
792    #[error("Image loading error {0}")]
793    Image(image::ImageError),
794    /// An error occurred during file loading.
795    #[error("A file load error has occurred {0:?}")]
796    FileLoadError(FileLoadError),
797}
798
799impl From<FileLoadError> for TextureError {
800    fn from(v: FileLoadError) -> Self {
801        Self::FileLoadError(v)
802    }
803}
804
805impl From<image::ImageError> for TextureError {
806    fn from(v: ImageError) -> Self {
807        Self::Image(v)
808    }
809}
810
811impl From<std::io::Error> for TextureError {
812    fn from(v: Error) -> Self {
813        Self::Io(v)
814    }
815}
816
817fn ceil_div_4(x: u32) -> u32 {
818    (x + 3) / 4
819}
820
821/// Texture compression options.
822///
823/// # Notes
824///
825/// Try to avoid using compression for normal maps, normals maps usually has smooth
826/// gradients, but compression algorithms used by rg3d cannot preserve good quality
827/// of such gradients.
828#[derive(
829    Copy,
830    Clone,
831    Deserialize,
832    Serialize,
833    PartialEq,
834    Eq,
835    Debug,
836    Inspect,
837    EnumVariantNames,
838    EnumString,
839    AsRefStr,
840)]
841#[repr(u32)]
842pub enum CompressionOptions {
843    /// An image will be stored without compression if it is not already compressed.
844    NoCompression = 0,
845
846    /// An image will be encoded via DXT1 (BC1) compression with low quality if is not
847    /// already compressed.
848    /// Compression ratio is 1:8 (without alpha) or 1:6 (with 1-bit alpha).
849    /// This option provides maximum speed by having lowest requirements of memory
850    /// bandwidth.
851    Speed = 1,
852
853    /// An image will be encoded via DXT5 (BC5) compression with high quality if it is
854    /// not already compressed.
855    /// Compression ratio is 1:4 (including alpha)
856    /// This option is faster than `NoCompression` speed by lower requirements of memory
857    /// bandwidth.
858    Quality = 2,
859}
860
861impl Default for CompressionOptions {
862    fn default() -> Self {
863        Self::Quality
864    }
865}
866
867fn transmute_slice<T>(bytes: &[u8]) -> &'_ [T] {
868    // This is absolutely safe because `image` crate's Rgb8/Rgba8/etc. and `tbc`s Rgb8/Rgba8/etc.
869    // have exactly the same memory layout.
870    unsafe {
871        std::slice::from_raw_parts(
872            bytes.as_ptr() as *const T,
873            bytes.len() / std::mem::size_of::<T>(),
874        )
875    }
876}
877
878fn compress_bc1<T: tbc::color::ColorRgba8>(bytes: &[u8], width: usize, height: usize) -> Vec<u8> {
879    tbc::encode_image_bc1_conv_u8::<T>(transmute_slice::<T>(bytes), width, height)
880}
881
882fn compress_bc3<T: tbc::color::ColorRgba8>(bytes: &[u8], width: usize, height: usize) -> Vec<u8> {
883    tbc::encode_image_bc3_conv_u8::<T>(transmute_slice::<T>(bytes), width, height)
884}
885
886fn compress_r8_bc4<T: tbc::color::ColorRed8>(bytes: &[u8], width: usize, height: usize) -> Vec<u8> {
887    tbc::encode_image_bc4_r8_conv_u8::<T>(transmute_slice::<T>(bytes), width, height)
888}
889
890fn compress_rg8_bc4<T: tbc::color::ColorRedGreen8>(
891    bytes: &[u8],
892    width: usize,
893    height: usize,
894) -> Vec<u8> {
895    tbc::encode_image_bc4_rg8_conv_u8::<T>(transmute_slice::<T>(bytes), width, height)
896}
897
898fn data_hash(data: &[u8]) -> u64 {
899    let mut hasher = FxHasher::default();
900    data.hash(&mut hasher);
901    hasher.finish()
902}
903
904fn try_compress(
905    image: &DynamicImage,
906    w: usize,
907    h: usize,
908    compression: CompressionOptions,
909) -> Option<(Vec<u8>, TexturePixelKind)> {
910    let bytes = image.as_bytes();
911    match (image, compression) {
912        (DynamicImage::ImageRgb8(_), CompressionOptions::Speed) => Some((
913            compress_bc1::<tbc::color::Rgb8>(bytes, w, h),
914            TexturePixelKind::DXT1RGB,
915        )),
916        (DynamicImage::ImageRgb8(_), CompressionOptions::Quality) => Some((
917            compress_bc3::<tbc::color::Rgb8>(bytes, w, h),
918            TexturePixelKind::DXT5RGBA,
919        )),
920        (DynamicImage::ImageRgba8(_), CompressionOptions::Speed) => Some((
921            compress_bc1::<tbc::color::Rgba8>(bytes, w, h),
922            TexturePixelKind::DXT1RGBA,
923        )),
924        (DynamicImage::ImageRgba8(_), CompressionOptions::Quality) => Some((
925            compress_bc3::<tbc::color::Rgba8>(bytes, w, h),
926            TexturePixelKind::DXT5RGBA,
927        )),
928        (DynamicImage::ImageLuma8(_), CompressionOptions::Speed)
929        | (DynamicImage::ImageLuma8(_), CompressionOptions::Quality) => Some((
930            compress_r8_bc4::<tbc::color::Red8>(bytes, w, h),
931            TexturePixelKind::R8RGTC,
932        )),
933        (DynamicImage::ImageLumaA8(_), CompressionOptions::Speed)
934        | (DynamicImage::ImageLumaA8(_), CompressionOptions::Quality) => Some((
935            compress_rg8_bc4::<tbc::color::RedGreen8>(bytes, w, h),
936            TexturePixelKind::RG8RGTC,
937        )),
938        _ => None,
939    }
940}
941
942fn bytes_in_first_mip(kind: TextureKind, pixel_kind: TexturePixelKind) -> u32 {
943    let pixel_count = match kind {
944        TextureKind::Line { length } => length,
945        TextureKind::Rectangle { width, height } => width * height,
946        TextureKind::Cube { width, height } => 6 * width * height,
947        TextureKind::Volume {
948            width,
949            height,
950            depth,
951        } => width * height * depth,
952    };
953    match pixel_kind {
954        // Uncompressed formats.
955        TexturePixelKind::R8 => pixel_count,
956        TexturePixelKind::R16 | TexturePixelKind::RG8 => 2 * pixel_count,
957        TexturePixelKind::RGB8 | TexturePixelKind::BGR8 => 3 * pixel_count,
958        TexturePixelKind::RGBA8 | TexturePixelKind::BGRA8 | TexturePixelKind::RG16 => {
959            4 * pixel_count
960        }
961        TexturePixelKind::RGB16 => 6 * pixel_count,
962        TexturePixelKind::RGBA16 => 8 * pixel_count,
963
964        // Compressed formats.
965        TexturePixelKind::DXT1RGB
966        | TexturePixelKind::DXT1RGBA
967        | TexturePixelKind::DXT3RGBA
968        | TexturePixelKind::DXT5RGBA
969        | TexturePixelKind::R8RGTC
970        | TexturePixelKind::RG8RGTC => {
971            let block_size = match pixel_kind {
972                TexturePixelKind::DXT1RGB
973                | TexturePixelKind::DXT1RGBA
974                | TexturePixelKind::R8RGTC => 8,
975                TexturePixelKind::DXT3RGBA
976                | TexturePixelKind::DXT5RGBA
977                | TexturePixelKind::RG8RGTC => 16,
978                _ => unreachable!(),
979            };
980            match kind {
981                TextureKind::Line { length } => ceil_div_4(length) * block_size,
982                TextureKind::Rectangle { width, height } => {
983                    ceil_div_4(width) * ceil_div_4(height) * block_size
984                }
985                TextureKind::Cube { width, height } => {
986                    6 * ceil_div_4(width) * ceil_div_4(height) * block_size
987                }
988                TextureKind::Volume {
989                    width,
990                    height,
991                    depth,
992                } => ceil_div_4(width) * ceil_div_4(height) * ceil_div_4(depth) * block_size,
993            }
994        }
995    }
996}
997
998impl TextureData {
999    /// Tries to load a texture from given data in one of the following formats: PNG, BMP, TGA, JPG, DDS, GIF. Use
1000    /// this method if you want to load a texture from embedded data.
1001    ///
1002    /// # On-demand compression and mip-map generation
1003    ///
1004    /// The data can be compressed if needed to improve performance on GPU side. Mip-maps can be generated as well.
1005    /// **CAVEAT:** Compression and mip-map generation **won't** be taken into account in case of **DDS** textures,
1006    /// because DDS can already contain such data, you should generate mips and compress DDS textures manually using
1007    /// some offline tool like DirectXTexTool or similar.
1008    ///
1009    /// # Important notes
1010    ///
1011    /// Textures loaded with this method won't be correctly serialized! It means that if you'll made a scene with
1012    /// textures loaded with this method, and then save a scene, then the engine won't be able to restore the textures
1013    /// if you'll try to load the saved scene. This is essential limitation of this method, because the engine does
1014    /// not know where to get the data of the texture at loading. You should use `ResourceManager::request_texture`
1015    /// in majority of cases!
1016    ///
1017    /// # Use cases
1018    ///
1019    /// Main use cases for this method are: procedural textures, icons for GUI.
1020    pub fn load_from_memory(
1021        data: &[u8],
1022        compression: CompressionOptions,
1023        gen_mip_maps: bool,
1024    ) -> Result<Self, TextureError> {
1025        // DDS is special. It can contain various kinds of textures as well as textures with
1026        // various pixel formats.
1027        //
1028        // TODO: Add support for DXGI formats.
1029        if let Ok(dds) = ddsfile::Dds::read(&mut Cursor::new(data)) {
1030            let d3dformat = dds
1031                .get_d3d_format()
1032                .ok_or(TextureError::UnsupportedFormat)?;
1033            let mip_count = dds.get_num_mipmap_levels();
1034            let mut bytes = dds.data;
1035
1036            // Try to use as much formats as possible.
1037            let pixel_kind = match d3dformat {
1038                D3DFormat::DXT1 => TexturePixelKind::DXT1RGBA,
1039                D3DFormat::DXT3 => TexturePixelKind::DXT3RGBA,
1040                D3DFormat::DXT5 => TexturePixelKind::DXT5RGBA,
1041                D3DFormat::L8 | D3DFormat::A8 => TexturePixelKind::R8,
1042                D3DFormat::L16 => TexturePixelKind::R16,
1043                D3DFormat::R8G8B8 => TexturePixelKind::RGB8,
1044                D3DFormat::A8L8 => TexturePixelKind::RG8,
1045                D3DFormat::A8R8G8B8 => {
1046                    // // ARGB8 -> RGBA8
1047                    // assert_eq!(bytes.len() % 4, 0);
1048                    // for chunk in bytes.chunks_exact_mut(4) {
1049                    //     let a = chunk[0];
1050                    //     let r = chunk[1];
1051                    //     let g = chunk[2];
1052                    //     let b = chunk[3];
1053                    //     chunk[0] = r;
1054                    //     chunk[1] = g;
1055                    //     chunk[2] = b;
1056                    //     chunk[3] = a;
1057                    // }
1058                    TexturePixelKind::RGBA8
1059                }
1060                D3DFormat::G16R16 => {
1061                    // GR16 -> RG16
1062                    assert_eq!(bytes.len() % 4, 0);
1063                    for chunk in bytes.chunks_exact_mut(4) {
1064                        // Red Hi + Lo bytes
1065                        let gh = chunk[0];
1066                        let gl = chunk[1];
1067                        // Green Hi + Lo bytes
1068                        let rh = chunk[2];
1069                        let rl = chunk[3];
1070                        // Swap
1071                        chunk[0] = rh;
1072                        chunk[1] = rl;
1073                        chunk[2] = gh;
1074                        chunk[3] = gl;
1075                    }
1076                    TexturePixelKind::RG16
1077                }
1078                _ => return Err(TextureError::UnsupportedFormat),
1079            };
1080
1081            Ok(Self {
1082                pixel_kind,
1083                data_hash: data_hash(&bytes),
1084                minification_filter: TextureMinificationFilter::LinearMipMapLinear,
1085                magnification_filter: TextureMagnificationFilter::Linear,
1086                s_wrap_mode: TextureWrapMode::Repeat,
1087                t_wrap_mode: TextureWrapMode::Repeat,
1088                mip_count,
1089                bytes: bytes.into(),
1090                kind: if dds.header.caps2 & Caps2::CUBEMAP == Caps2::CUBEMAP {
1091                    TextureKind::Cube {
1092                        width: dds.header.width,
1093                        height: dds.header.height,
1094                    }
1095                } else if dds.header.caps2 & Caps2::VOLUME == Caps2::VOLUME {
1096                    TextureKind::Volume {
1097                        width: dds.header.width,
1098                        height: dds.header.height,
1099                        depth: dds.header.depth.unwrap(),
1100                    }
1101                } else {
1102                    TextureKind::Rectangle {
1103                        width: dds.header.width,
1104                        height: dds.header.height,
1105                    }
1106                },
1107                ..Default::default()
1108            })
1109        } else {
1110            // Commonly used formats are all rectangle textures.
1111            let dyn_img = image::load_from_memory(data)
1112                // Try to load as TGA, this is needed because TGA is badly designed format and does not
1113                // have an identifier in the beginning of the file (so called "magic") that allows quickly
1114                // check if the file is really contains expected data.
1115                .or_else(|_| image::load_from_memory_with_format(data, ImageFormat::Tga))?;
1116
1117            let width = dyn_img.width();
1118            let height = dyn_img.height();
1119
1120            let mut pixel_kind = match dyn_img {
1121                DynamicImage::ImageLuma8(_) => TexturePixelKind::R8,
1122                DynamicImage::ImageLumaA8(_) => TexturePixelKind::RG8,
1123                DynamicImage::ImageRgb8(_) => TexturePixelKind::RGB8,
1124                DynamicImage::ImageRgba8(_) => TexturePixelKind::RGBA8,
1125                DynamicImage::ImageBgr8(_) => TexturePixelKind::BGR8,
1126                DynamicImage::ImageBgra8(_) => TexturePixelKind::BGRA8,
1127                DynamicImage::ImageLuma16(_) => TexturePixelKind::R16,
1128                DynamicImage::ImageLumaA16(_) => TexturePixelKind::RG16,
1129                DynamicImage::ImageRgb16(_) => TexturePixelKind::RGB16,
1130                DynamicImage::ImageRgba16(_) => TexturePixelKind::RGBA16,
1131            };
1132
1133            let mut mip_count = 0;
1134            let mut bytes = Vec::new();
1135
1136            if gen_mip_maps {
1137                let mut level_width = width;
1138                let mut level_height = height;
1139                let mut current_level = dyn_img;
1140
1141                while level_width != 0 && level_height != 0 {
1142                    if mip_count != 0 {
1143                        current_level = current_level.resize_exact(
1144                            level_width,
1145                            level_height,
1146                            FilterType::Lanczos3,
1147                        );
1148                    }
1149
1150                    mip_count += 1;
1151
1152                    if compression == CompressionOptions::NoCompression {
1153                        bytes.extend_from_slice(current_level.as_bytes())
1154                    } else if let Some((compressed_data, new_pixel_kind)) = try_compress(
1155                        &current_level,
1156                        level_width as usize,
1157                        level_height as usize,
1158                        compression,
1159                    ) {
1160                        pixel_kind = new_pixel_kind;
1161                        bytes.extend_from_slice(&compressed_data);
1162                    } else {
1163                        bytes.extend_from_slice(current_level.as_bytes())
1164                    }
1165
1166                    level_width = level_width.checked_shr(1).unwrap_or_default();
1167                    level_height = level_height.checked_shr(1).unwrap_or_default();
1168                }
1169            } else {
1170                mip_count = 1;
1171
1172                if compression == CompressionOptions::NoCompression {
1173                    bytes.extend_from_slice(dyn_img.as_bytes());
1174                } else if let Some((compressed_data, new_pixel_kind)) =
1175                    try_compress(&dyn_img, width as usize, height as usize, compression)
1176                {
1177                    pixel_kind = new_pixel_kind;
1178                    bytes.extend_from_slice(&compressed_data);
1179                } else {
1180                    bytes.extend_from_slice(dyn_img.as_bytes())
1181                }
1182            }
1183
1184            Ok(Self {
1185                pixel_kind,
1186                kind: TextureKind::Rectangle { width, height },
1187                data_hash: data_hash(&bytes),
1188                bytes: bytes.into(),
1189                mip_count,
1190                ..Default::default()
1191            })
1192        }
1193    }
1194
1195    /// Tries to load a texture from a file.
1196    ///
1197    /// # Notes
1198    ///
1199    /// It is **not** public because you must use resource manager to load textures from external
1200    /// resources.
1201    pub(in crate) async fn load_from_file<P: AsRef<Path>>(
1202        path: P,
1203        compression: CompressionOptions,
1204        gen_mip_maps: bool,
1205    ) -> Result<Self, TextureError> {
1206        let data = io::load_file(path.as_ref()).await?;
1207        let mut texture = Self::load_from_memory(&data, compression, gen_mip_maps)?;
1208        texture.path = path.as_ref().to_path_buf();
1209        Ok(texture)
1210    }
1211
1212    /// Creates new texture instance from given parameters.
1213    ///
1214    /// # Limitations
1215    ///
1216    /// Currently textures with only one mip level are supported!
1217    pub fn from_bytes(
1218        kind: TextureKind,
1219        pixel_kind: TexturePixelKind,
1220        bytes: Vec<u8>,
1221        serialize_content: bool,
1222    ) -> Option<Self> {
1223        if bytes_in_first_mip(kind, pixel_kind) != bytes.len() as u32 {
1224            None
1225        } else {
1226            Some(Self {
1227                path: Default::default(),
1228                kind,
1229                data_hash: data_hash(&bytes),
1230                bytes: bytes.into(),
1231                pixel_kind,
1232                serialize_content,
1233                ..Default::default()
1234            })
1235        }
1236    }
1237
1238    /// Sets new minification filter. It is used when texture becomes smaller.
1239    pub fn set_minification_filter(&mut self, filter: TextureMinificationFilter) {
1240        self.minification_filter = filter;
1241    }
1242
1243    /// Returns current minification filter.
1244    pub fn minification_filter(&self) -> TextureMinificationFilter {
1245        self.minification_filter
1246    }
1247
1248    /// Sets new magnification filter. It is used when texture is "stretching".
1249    pub fn set_magnification_filter(&mut self, filter: TextureMagnificationFilter) {
1250        self.magnification_filter = filter;
1251    }
1252
1253    /// Returns current magnification filter.
1254    pub fn magnification_filter(&self) -> TextureMagnificationFilter {
1255        self.magnification_filter
1256    }
1257
1258    /// Sets new S coordinate wrap mode.
1259    pub fn set_s_wrap_mode(&mut self, s_wrap_mode: TextureWrapMode) {
1260        self.s_wrap_mode = s_wrap_mode;
1261    }
1262
1263    /// Returns current S coordinate wrap mode.
1264    pub fn s_wrap_mode(&self) -> TextureWrapMode {
1265        self.s_wrap_mode
1266    }
1267
1268    /// Sets new T coordinate wrap mode.
1269    pub fn set_t_wrap_mode(&mut self, t_wrap_mode: TextureWrapMode) {
1270        self.t_wrap_mode = t_wrap_mode;
1271    }
1272
1273    /// Returns current T coordinate wrap mode.
1274    pub fn t_wrap_mode(&self) -> TextureWrapMode {
1275        self.t_wrap_mode
1276    }
1277
1278    /// Returns total mip count.
1279    pub fn mip_count(&self) -> u32 {
1280        self.mip_count
1281    }
1282
1283    /// Returns texture kind.
1284    pub fn kind(&self) -> TextureKind {
1285        self.kind
1286    }
1287
1288    /// Returns current data hash. Hash is guaranteed to be in actual state.
1289    pub fn data_hash(&self) -> u64 {
1290        self.data_hash
1291    }
1292
1293    /// Returns current pixel kind.
1294    pub fn pixel_kind(&self) -> TexturePixelKind {
1295        self.pixel_kind
1296    }
1297
1298    /// Returns current data as immutable slice.
1299    pub fn data(&self) -> &[u8] {
1300        &self.bytes
1301    }
1302
1303    /// Returns data of the first mip level.
1304    pub fn first_mip_level_data(&self) -> &[u8] {
1305        &self.bytes[0..bytes_in_first_mip(self.kind, self.pixel_kind) as usize]
1306    }
1307
1308    /// Returns true if the texture is procedural, false - otherwise.
1309    ///
1310    /// # Notes
1311    ///
1312    /// Content of procedural textures is saved during serialization and they never resolved
1313    /// on deserialization. Resolving here means a process of getting correct texture instance
1314    /// by its path.
1315    pub fn is_procedural(&self) -> bool {
1316        self.serialize_content
1317    }
1318
1319    /// Returns true if the texture is used as render target.
1320    pub fn is_render_target(&self) -> bool {
1321        self.is_render_target
1322    }
1323
1324    /// Max samples for anisotropic filtering. Default value is 16.0 (max).
1325    /// However real value passed to GPU will be clamped to maximum supported
1326    /// by current GPU. To disable anisotropic filtering set this to 1.0.
1327    /// Typical values are 2.0, 4.0, 8.0, 16.0.
1328    pub fn set_anisotropy_level(&mut self, anisotropy: f32) {
1329        self.anisotropy = anisotropy.max(1.0);
1330    }
1331
1332    /// Returns current anisotropy level.
1333    pub fn anisotropy_level(&self) -> f32 {
1334        self.anisotropy
1335    }
1336
1337    /// Sets new path to source file.
1338    pub fn set_path<P: AsRef<Path>>(&mut self, path: P) {
1339        self.path = path.as_ref().to_owned();
1340    }
1341
1342    /// Tries to save internal buffer into source file.
1343    pub fn save(&self) -> Result<(), TextureError> {
1344        let color_type = match self.pixel_kind {
1345            TexturePixelKind::R8 => ColorType::L8,
1346            TexturePixelKind::RGB8 => ColorType::Rgb8,
1347            TexturePixelKind::RGBA8 => ColorType::Rgba8,
1348            TexturePixelKind::RG8 => ColorType::La8,
1349            TexturePixelKind::R16 => ColorType::L16,
1350            TexturePixelKind::RG16 => ColorType::La16,
1351            TexturePixelKind::BGR8 => ColorType::Bgr8,
1352            TexturePixelKind::BGRA8 => ColorType::Bgra8,
1353            TexturePixelKind::RGB16 => ColorType::Rgb16,
1354            TexturePixelKind::RGBA16 => ColorType::Rgba16,
1355            TexturePixelKind::DXT1RGB
1356            | TexturePixelKind::DXT1RGBA
1357            | TexturePixelKind::DXT3RGBA
1358            | TexturePixelKind::DXT5RGBA
1359            | TexturePixelKind::R8RGTC
1360            | TexturePixelKind::RG8RGTC => return Err(TextureError::UnsupportedFormat),
1361        };
1362        if let TextureKind::Rectangle { width, height } = self.kind {
1363            Ok(image::save_buffer(
1364                &self.path,
1365                self.bytes.as_ref(),
1366                width,
1367                height,
1368                color_type,
1369            )?)
1370        } else {
1371            Err(TextureError::UnsupportedFormat)
1372        }
1373    }
1374
1375    /// Returns a special reference holder that provides mutable access to content of the
1376    /// texture and automatically calculates hash of the data in its destructor.
1377    pub fn modify(&mut self) -> TextureDataRefMut<'_> {
1378        TextureDataRefMut { texture: self }
1379    }
1380}
1381
1382/// A special reference holder that provides mutable access to content of the
1383/// texture and automatically calculates hash of the data in its destructor.
1384pub struct TextureDataRefMut<'a> {
1385    texture: &'a mut TextureData,
1386}
1387
1388impl<'a> Drop for TextureDataRefMut<'a> {
1389    fn drop(&mut self) {
1390        self.texture.data_hash = data_hash(&self.texture.bytes);
1391    }
1392}
1393
1394impl<'a> Deref for TextureDataRefMut<'a> {
1395    type Target = TextureData;
1396
1397    fn deref(&self) -> &Self::Target {
1398        self.texture
1399    }
1400}
1401
1402impl<'a> DerefMut for TextureDataRefMut<'a> {
1403    fn deref_mut(&mut self) -> &mut Self::Target {
1404        self.texture
1405    }
1406}
1407
1408impl<'a> TextureDataRefMut<'a> {
1409    /// Returns mutable reference to the data of the texture.
1410    pub fn data_mut(&mut self) -> &mut [u8] {
1411        &mut self.texture.bytes
1412    }
1413}