1use 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#[derive(Copy, Clone, Debug)]
51pub enum TextureKind {
52 Line {
54 length: u32,
56 },
57 Rectangle {
59 width: u32,
61 height: u32,
63 },
64 Cube {
66 width: u32,
68 height: u32,
70 },
71 Volume {
73 width: u32,
75 height: u32,
77 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#[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 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#[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 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 pub fn set_minification_filter(&mut self, minification_filter: TextureMinificationFilter) {
336 self.minification_filter = minification_filter;
337 }
338
339 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 pub fn set_magnification_filter(&mut self, magnification_filter: TextureMagnificationFilter) {
352 self.magnification_filter = magnification_filter;
353 }
354
355 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 pub fn set_s_wrap_mode(&mut self, s_wrap_mode: TextureWrapMode) {
365 self.s_wrap_mode = s_wrap_mode;
366 }
367
368 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 pub fn set_t_wrap_mode(&mut self, t_wrap_mode: TextureWrapMode) {
378 self.t_wrap_mode = t_wrap_mode;
379 }
380
381 pub fn with_anisotropy(mut self, anisotropy: f32) -> Self {
384 self.anisotropy = anisotropy.min(1.0);
385 self
386 }
387
388 pub fn set_anisotropy(&mut self, anisotropy: f32) {
391 self.anisotropy = anisotropy.min(1.0);
392 }
393
394 pub fn with_compression(mut self, compression: CompressionOptions) -> Self {
396 self.compression = compression;
397 self
398 }
399
400 pub fn set_compression(&mut self, compression: CompressionOptions) {
402 self.compression = compression;
403 }
404}
405
406define_new_resource!(
407 Texture<TextureData, TextureError>
409);
410
411pub type TextureState = ResourceState<TextureData, TextureError>;
413
414impl Texture {
415 pub fn new_render_target(width: u32, height: u32) -> Self {
419 Self(Resource::new(TextureState::Ok(TextureData {
420 path: Default::default(),
421 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 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 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#[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 Nearest = 0,
499
500 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#[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 Nearest = 0,
556
557 NearestMipMapNearest = 1,
561
562 NearestMipMapLinear = 2,
567
568 Linear = 3,
571
572 LinearMipMapNearest = 4,
576
577 LinearMipMapLinear = 5,
582}
583
584impl TextureMinificationFilter {
585 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#[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 Repeat = 0,
651
652 ClampToEdge = 1,
655
656 ClampToBorder = 2,
660
661 MirroredRepeat = 3,
665
666 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#[derive(Copy, Clone, PartialEq, Eq, Debug)]
703#[repr(u32)]
704pub enum TexturePixelKind {
705 R8 = 0,
707
708 RGB8 = 1,
710
711 RGBA8 = 2,
713
714 RG8 = 3,
716
717 R16 = 4,
719
720 RG16 = 5,
722
723 BGR8 = 6,
725
726 BGRA8 = 7,
728
729 RGB16 = 8,
731
732 RGBA16 = 9,
734
735 DXT1RGB = 10,
737
738 DXT1RGBA = 11,
740
741 DXT3RGBA = 12,
743
744 DXT5RGBA = 13,
746
747 R8RGTC = 14,
749
750 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#[derive(Debug, thiserror::Error)]
784pub enum TextureError {
785 #[error("Unsupported format!")]
787 UnsupportedFormat,
788 #[error("An i/o error has occurred: {0}")]
790 Io(std::io::Error),
791 #[error("Image loading error {0}")]
793 Image(image::ImageError),
794 #[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#[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 NoCompression = 0,
845
846 Speed = 1,
852
853 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 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 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 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 pub fn load_from_memory(
1021 data: &[u8],
1022 compression: CompressionOptions,
1023 gen_mip_maps: bool,
1024 ) -> Result<Self, TextureError> {
1025 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 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 TexturePixelKind::RGBA8
1059 }
1060 D3DFormat::G16R16 => {
1061 assert_eq!(bytes.len() % 4, 0);
1063 for chunk in bytes.chunks_exact_mut(4) {
1064 let gh = chunk[0];
1066 let gl = chunk[1];
1067 let rh = chunk[2];
1069 let rl = chunk[3];
1070 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 let dyn_img = image::load_from_memory(data)
1112 .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 ¤t_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 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 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 pub fn set_minification_filter(&mut self, filter: TextureMinificationFilter) {
1240 self.minification_filter = filter;
1241 }
1242
1243 pub fn minification_filter(&self) -> TextureMinificationFilter {
1245 self.minification_filter
1246 }
1247
1248 pub fn set_magnification_filter(&mut self, filter: TextureMagnificationFilter) {
1250 self.magnification_filter = filter;
1251 }
1252
1253 pub fn magnification_filter(&self) -> TextureMagnificationFilter {
1255 self.magnification_filter
1256 }
1257
1258 pub fn set_s_wrap_mode(&mut self, s_wrap_mode: TextureWrapMode) {
1260 self.s_wrap_mode = s_wrap_mode;
1261 }
1262
1263 pub fn s_wrap_mode(&self) -> TextureWrapMode {
1265 self.s_wrap_mode
1266 }
1267
1268 pub fn set_t_wrap_mode(&mut self, t_wrap_mode: TextureWrapMode) {
1270 self.t_wrap_mode = t_wrap_mode;
1271 }
1272
1273 pub fn t_wrap_mode(&self) -> TextureWrapMode {
1275 self.t_wrap_mode
1276 }
1277
1278 pub fn mip_count(&self) -> u32 {
1280 self.mip_count
1281 }
1282
1283 pub fn kind(&self) -> TextureKind {
1285 self.kind
1286 }
1287
1288 pub fn data_hash(&self) -> u64 {
1290 self.data_hash
1291 }
1292
1293 pub fn pixel_kind(&self) -> TexturePixelKind {
1295 self.pixel_kind
1296 }
1297
1298 pub fn data(&self) -> &[u8] {
1300 &self.bytes
1301 }
1302
1303 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 pub fn is_procedural(&self) -> bool {
1316 self.serialize_content
1317 }
1318
1319 pub fn is_render_target(&self) -> bool {
1321 self.is_render_target
1322 }
1323
1324 pub fn set_anisotropy_level(&mut self, anisotropy: f32) {
1329 self.anisotropy = anisotropy.max(1.0);
1330 }
1331
1332 pub fn anisotropy_level(&self) -> f32 {
1334 self.anisotropy
1335 }
1336
1337 pub fn set_path<P: AsRef<Path>>(&mut self, path: P) {
1339 self.path = path.as_ref().to_owned();
1340 }
1341
1342 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 pub fn modify(&mut self) -> TextureDataRefMut<'_> {
1378 TextureDataRefMut { texture: self }
1379 }
1380}
1381
1382pub 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 pub fn data_mut(&mut self) -> &mut [u8] {
1411 &mut self.texture.bytes
1412 }
1413}