glacier_texture/
image.rs

1use crate::atlas::AtlasData;
2use crate::convert::create_dds;
3use crate::convert::TextureConversionError::DirectXTexError;
4use crate::enums::RenderFormat;
5use crate::mipblock::MipblockData;
6use crate::pack::{TextureMapBuilder, TextureMapParameters, TexturePackerError};
7use crate::texture_map::{TextureMap};
8use crate::WoaVersion;
9use binrw::BinRead;
10use directxtex::{HResultError, ScratchImage, CP_FLAGS, DDS_FLAGS, DXGI_FORMAT, TEX_FILTER_FLAGS};
11use image::error::{EncodingError, ImageFormatHint};
12use image::{ColorType, ExtendedColorType, ImageDecoder, ImageEncoder, ImageError, ImageResult};
13use std::io::{BufRead, Seek, Write};
14use thiserror::Error;
15
16#[derive(Debug, Error)]
17pub enum TextureMapEncodeError {
18    #[error("DXGI conversion failed for color type {0:?}")]
19    DxgiConversion(ExtendedColorType),
20    #[error("Failed DirectXTex operation {0}")]
21    DirectXTexError(#[from] HResultError),
22    #[error("Failed to pack texture")]
23    Packer(#[from] TexturePackerError),
24    #[error("IO error {0}")]
25    IOError(#[from] std::io::Error),
26}
27
28impl From<TextureMapEncodeError> for ImageError {
29    fn from(e: TextureMapEncodeError) -> Self {
30        ImageError::Encoding(EncodingError::new(
31            ImageFormatHint::Name("TextureMap".to_owned()),
32            e.to_string(),
33        ))
34    }
35}
36
37pub struct TextureMapEncoder<TW: Write, DW: Write> {
38    text_writer: TW,
39    texd_writer: Option<DW>,
40    woa_version: WoaVersion,
41    texture_parameters: Option<TextureMapParameters>,
42    atlas_data: Option<AtlasData>,
43}
44
45impl<TW: Write, DW: Write> TextureMapEncoder<TW, DW> {
46    pub fn new(
47        text_writer: TW,
48        texd_writer: Option<DW>,
49        woa_version: WoaVersion,
50        texture_parameters: Option<TextureMapParameters>,
51        atlas_data: Option<AtlasData>,
52    ) -> TextureMapEncoder<TW, DW> {
53        TextureMapEncoder {
54            text_writer,
55            texd_writer,
56            woa_version,
57            texture_parameters,
58            atlas_data,
59        }
60    }
61}
62
63impl<TW: Write, DW: Write> ImageEncoder for TextureMapEncoder<TW, DW> {
64    fn write_image(
65        self,
66        buf: &[u8],
67        width: u32,
68        height: u32,
69        color_type: ExtendedColorType,
70    ) -> ImageResult<()> {
71        let scratch_image = dynamic_image_to_scratch_image(buf, width, height, color_type)?;
72        let mut builder = TextureMapBuilder::from_scratch_image(scratch_image)
73            .map_err(TextureMapEncodeError::Packer)?;
74
75        if let Some(params) = self.texture_parameters {
76            builder = builder.with_params(params);
77        }
78
79        if let Some(atlas_data) = self.atlas_data {
80            builder = builder.with_atlas(atlas_data);
81        }
82
83        let text = builder
84            .build(self.woa_version)
85            .map_err(TextureMapEncodeError::Packer)?;
86        let text_data = text.pack_to_vec().map_err(TextureMapEncodeError::Packer)?;
87
88        let mut text_writer = self.text_writer;
89        text_writer.write_all(&text_data)?;
90
91        if let Some(mut texd_writer) = self.texd_writer {
92            if let Some(texd) = text.mipblock1() {
93                let texd_data = texd
94                    .pack_to_vec(self.woa_version)
95                    .map_err(TextureMapEncodeError::Packer)?;
96                texd_writer.write_all(&texd_data)?;
97            }
98        }
99        Ok(())
100    }
101}
102
103pub fn dynamic_image_to_scratch_image(buf: &[u8], width: u32, height: u32, color_type: ExtendedColorType) -> Result<ScratchImage, TextureMapEncodeError> {
104    let dxgi_format = helpers::color_type_to_dxgi(color_type)
105        .ok_or(TextureMapEncodeError::DxgiConversion(color_type))?;
106    let slice_pitch = dxgi_format
107        .compute_pitch(width as usize, height as usize, CP_FLAGS::CP_FLAGS_NONE)
108        .map_err(TextureMapEncodeError::DirectXTexError)?;
109
110    let width = width as usize;
111    let height = height as usize;
112
113    let maybe_converted;
114    let pixels = match color_type {
115        ExtendedColorType::Rgb8 | ExtendedColorType::Bgr8 => {
116            maybe_converted = Some(helpers::rgb8_to_rgba8(buf));
117            maybe_converted.as_ref().unwrap().as_ptr() as *mut u8
118        }
119        ExtendedColorType::Rgb16 => {
120            maybe_converted = Some(helpers::rgb16_to_rgba16(buf));
121            maybe_converted.as_ref().unwrap().as_ptr() as *mut u8
122        }
123        _ => buf.as_ptr() as *mut u8,
124    };
125
126    let image = directxtex::Image {
127        width,
128        height,
129        format: dxgi_format,
130        row_pitch: slice_pitch.row,
131        slice_pitch: slice_pitch.slice,
132        pixels,
133    };
134
135    image
136        .resize(width, height, TEX_FILTER_FLAGS::TEX_FILTER_DEFAULT | TEX_FILTER_FLAGS::TEX_FILTER_FORCE_NON_WIC)
137        .map_err(TextureMapEncodeError::DirectXTexError)
138}
139
140
141pub struct TextureMapDecoder {
142    texture: TextureMap,
143}
144
145impl TextureMapDecoder {
146    pub fn new<TR: BufRead + Seek, DR: BufRead + Seek>(
147        mut text_reader: TR,
148        texd_reader: Option<DR>,
149        woa_version: WoaVersion,
150    ) -> Self {
151        let mut texture = TextureMap::read_le_args(&mut text_reader, (woa_version,)).unwrap();
152        if let Some(mut texd_reader) = texd_reader {
153            let mut buf = Vec::new();
154            texd_reader.read_to_end(&mut buf).unwrap();
155            let mip_data = MipblockData::from_memory(&buf, woa_version).unwrap();
156            texture.set_mipblock1(mip_data);
157        }
158        Self { texture }
159    }
160
161    pub fn from_texture_map(texture: TextureMap) -> Self {
162        Self { texture }
163    }
164}
165
166impl ImageDecoder for TextureMapDecoder {
167    fn dimensions(&self) -> (u32, u32) {
168        (self.texture.width() as u32, self.texture.height() as u32)
169    }
170
171    fn color_type(&self) -> ColorType {
172        match self.texture.format() {
173            RenderFormat::R16G16B16A16 => ColorType::Rgba16,
174            RenderFormat::R8G8B8A8 => ColorType::Rgba8,
175            RenderFormat::R8G8 => ColorType::La8,
176            RenderFormat::A8 => ColorType::L8,
177            RenderFormat::BC1 => ColorType::Rgba8,
178            RenderFormat::BC2 => ColorType::Rgba8,
179            RenderFormat::BC3 => ColorType::Rgba8,
180            RenderFormat::BC4 => ColorType::L8,
181            RenderFormat::BC5 => ColorType::La8,
182            RenderFormat::BC7 => ColorType::Rgba8,
183        }
184    }
185
186    fn read_image(self, buf: &mut [u8]) -> ImageResult<()>
187    where
188        Self: Sized,
189    {
190        let dds = create_dds(&self.texture).unwrap();
191        let mut scratch_image = ScratchImage::load_dds(
192            dds.as_slice(),
193            DDS_FLAGS::DDS_FLAGS_FORCE_DX10_EXT,
194            None,
195            None,
196        )
197        .map_err(DirectXTexError)
198        .unwrap();
199
200        scratch_image = crate::convert::decompress_dds(&self.texture, scratch_image).unwrap();
201
202        let blob = scratch_image
203            .image(0, 0, 0)
204            .unwrap()
205            .save_dds(DDS_FLAGS::DDS_FLAGS_FORCE_DX10_EXT)
206            .unwrap();
207
208        let data = blob.buffer();
209        buf.copy_from_slice(&data[data.len() - buf.len()..]);
210
211        Ok(())
212    }
213
214    fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
215        (*self).read_image(buf)
216    }
217}
218
219mod helpers {
220    use super::*;
221    pub(super) fn color_type_to_dxgi(color_type: ExtendedColorType) -> Option<DXGI_FORMAT> {
222        match color_type {
223            ExtendedColorType::A8 => Some(DXGI_FORMAT::DXGI_FORMAT_A8_UNORM),
224            ExtendedColorType::L8 => Some(DXGI_FORMAT::DXGI_FORMAT_R8_UNORM),
225            ExtendedColorType::La8 => Some(DXGI_FORMAT::DXGI_FORMAT_R8G8_UNORM),
226            ExtendedColorType::Rgb8 => Some(DXGI_FORMAT::DXGI_FORMAT_R8G8B8A8_UNORM), // Needs additional alpha channel
227            ExtendedColorType::Rgba8 => Some(DXGI_FORMAT::DXGI_FORMAT_R8G8B8A8_UNORM),
228            ExtendedColorType::L16 => Some(DXGI_FORMAT::DXGI_FORMAT_R16_UNORM),
229            ExtendedColorType::La16 => Some(DXGI_FORMAT::DXGI_FORMAT_R16G16_UNORM),
230            ExtendedColorType::Rgb16 => Some(DXGI_FORMAT::DXGI_FORMAT_R16G16B16A16_UNORM), // Needs additional alpha channel
231            ExtendedColorType::Rgba16 => Some(DXGI_FORMAT::DXGI_FORMAT_R16G16B16A16_UNORM),
232            ExtendedColorType::Bgr8 => Some(DXGI_FORMAT::DXGI_FORMAT_B8G8R8X8_UNORM), // Needs additional alpha channel
233            ExtendedColorType::Bgra8 => Some(DXGI_FORMAT::DXGI_FORMAT_B8G8R8A8_UNORM),
234            ExtendedColorType::Rgb32F => Some(DXGI_FORMAT::DXGI_FORMAT_R32G32B32_FLOAT),
235            ExtendedColorType::Rgba32F => Some(DXGI_FORMAT::DXGI_FORMAT_R32G32B32A32_FLOAT),
236            _ => None,
237        }
238    }
239
240    pub(super) fn rgb8_to_rgba8(rgb: &[u8]) -> Vec<u8> {
241        let mut rgba = Vec::with_capacity(rgb.len() / 3 * 4);
242        for chunk in rgb.chunks(3) {
243            rgba.push(chunk[0]);
244            rgba.push(chunk[1]);
245            rgba.push(chunk[2]);
246            rgba.push(0xFF);
247        }
248        rgba
249    }
250
251    pub(super) fn rgb16_to_rgba16(rgb: &[u8]) -> Vec<u8> {
252        assert_eq!(rgb.len() % 6, 0, "Input length must be divisible by 6.");
253        let mut rgba = Vec::with_capacity(rgb.len() / 3 * 4);
254        for chunk in rgb.chunks(6) {
255            rgba.extend_from_slice(&chunk[0..2]);
256            rgba.extend_from_slice(&chunk[2..4]);
257            rgba.extend_from_slice(&chunk[4..6]);
258            rgba.extend_from_slice(&0xFFFFu16.to_le_bytes());
259        }
260        rgba
261    }
262}