1use crate::atlas::AtlasData;
2use crate::box_reflection::{BoxReflection, BoxReflectionError, CubemapLayout};
3use crate::convert::create_dds;
4use crate::convert::TextureConversionError::DirectXTexError;
5use crate::enums::RenderFormat;
6use crate::mipblock::MipblockData;
7use crate::pack::{TextureMapBuilder, TextureMapParameters, TexturePackerError};
8use crate::texture_map::TextureMap;
9use crate::GlacierGame;
10use binrw::BinRead;
11use directxtex::{
12 HResultError, ScratchImage, CP_FLAGS, DDS_FLAGS, DXGI_FORMAT, DXGI_FORMAT_R16G16B16A16_FLOAT,
13 DXGI_FORMAT_R32G32B32A32_FLOAT, TEX_FILTER_FLAGS, TEX_THRESHOLD_DEFAULT,
14};
15use image::error::{EncodingError, ImageFormatHint};
16use image::{ColorType, ExtendedColorType, ImageDecoder, ImageEncoder, ImageError, ImageResult};
17use std::io::{BufRead, Seek, Write};
18use thiserror::Error;
19
20#[derive(Debug, Error)]
21pub enum TextureMapEncodeError {
22 #[error("DXGI conversion failed for color type {0:?}")]
23 DxgiConversion(ExtendedColorType),
24 #[error("Failed DirectXTex operation {0}")]
25 DirectXTexError(#[from] HResultError),
26 #[error("Failed to pack texture")]
27 Packer(#[from] TexturePackerError),
28 #[error("IO error {0}")]
29 IOError(#[from] std::io::Error),
30}
31
32impl From<TextureMapEncodeError> for ImageError {
33 fn from(e: TextureMapEncodeError) -> Self {
34 ImageError::Encoding(EncodingError::new(
35 ImageFormatHint::Name("TextureMap".to_owned()),
36 e.to_string(),
37 ))
38 }
39}
40
41pub struct TextureMapEncoder<TW: Write, DW: Write> {
42 text_writer: TW,
43 texd_writer: Option<DW>,
44 glacier_game: GlacierGame,
45 texture_parameters: Option<TextureMapParameters>,
46 atlas_data: Option<AtlasData>,
47}
48
49impl<TW: Write, DW: Write> TextureMapEncoder<TW, DW> {
50 pub fn new(
51 text_writer: TW,
52 texd_writer: Option<DW>,
53 glacier_game: GlacierGame,
54 texture_parameters: Option<TextureMapParameters>,
55 atlas_data: Option<AtlasData>,
56 ) -> TextureMapEncoder<TW, DW> {
57 TextureMapEncoder {
58 text_writer,
59 texd_writer,
60 glacier_game,
61 texture_parameters,
62 atlas_data,
63 }
64 }
65}
66
67impl<TW: Write, DW: Write> ImageEncoder for TextureMapEncoder<TW, DW> {
68 fn write_image(
69 self,
70 buf: &[u8],
71 width: u32,
72 height: u32,
73 color_type: ExtendedColorType,
74 ) -> ImageResult<()> {
75 let scratch_image =
76 helpers::dynamic_image_to_scratch_image(buf, width, height, color_type)?;
77 let mut builder = TextureMapBuilder::from_scratch_image(scratch_image)
78 .map_err(TextureMapEncodeError::Packer)?;
79
80 if let Some(params) = self.texture_parameters {
81 builder = builder.with_params(params);
82 }
83
84 if let Some(atlas_data) = self.atlas_data {
85 builder = builder.with_atlas(atlas_data);
86 }
87
88 let text = builder
89 .build(self.glacier_game)
90 .map_err(TextureMapEncodeError::Packer)?;
91 let text_data = text.pack_to_vec().map_err(TextureMapEncodeError::Packer)?;
92
93 let mut text_writer = self.text_writer;
94 text_writer.write_all(&text_data)?;
95
96 if let Some(mut texd_writer) = self.texd_writer {
97 if let Some(texd) = text.mipblock1() {
98 let texd_data = texd
99 .pack_to_vec(self.glacier_game)
100 .map_err(TextureMapEncodeError::Packer)?;
101 texd_writer.write_all(&texd_data)?;
102 }
103 }
104 Ok(())
105 }
106}
107
108#[derive(Debug, Error)]
109pub enum BoxReflectionEncodeError {
110 #[error("DXGI conversion failed for color type {0:?}")]
111 DxgiConversion(ExtendedColorType),
112 #[error("Failed DirectXTex operation {0}")]
113 DirectXTexError(#[from] HResultError),
114 #[error("Failed to pack box reflection")]
115 BoxReflection(#[from] BoxReflectionError),
116 #[error("IO error {0}")]
117 IOError(#[from] std::io::Error),
118 #[error("Binrw error {0}")]
119 Binrw(#[from] binrw::Error),
120}
121
122impl From<BoxReflectionEncodeError> for ImageError {
123 fn from(e: BoxReflectionEncodeError) -> Self {
124 ImageError::Encoding(EncodingError::new(
125 ImageFormatHint::Name("BoxReflection".to_owned()),
126 e.to_string(),
127 ))
128 }
129}
130
131pub struct TextureMapDecoder {
132 texture: TextureMap,
133}
134
135impl TextureMapDecoder {
136 pub fn new<TR: BufRead + Seek, DR: BufRead + Seek>(
137 mut text_reader: TR,
138 texd_reader: Option<DR>,
139 glacier_game: GlacierGame,
140 ) -> Self {
141 let mut texture = TextureMap::read_le_args(&mut text_reader, (glacier_game,)).unwrap();
142 if let Some(mut texd_reader) = texd_reader {
143 let mut buf = Vec::new();
144 texd_reader.read_to_end(&mut buf).unwrap();
145 let mip_data = MipblockData::from_memory(&buf, glacier_game).unwrap();
146 texture.set_mipblock1(mip_data);
147 }
148 Self { texture }
149 }
150
151 pub fn from_texture_map(texture: TextureMap) -> Self {
152 Self { texture }
153 }
154}
155
156impl ImageDecoder for TextureMapDecoder {
157 fn dimensions(&self) -> (u32, u32) {
158 (self.texture.width() as u32, self.texture.height() as u32)
159 }
160
161 fn color_type(&self) -> ColorType {
162 match self.texture.format() {
163 RenderFormat::R32G32B32A32 => ColorType::Rgba32F,
164 RenderFormat::R16G16B16A16 => ColorType::Rgba32F,
165 RenderFormat::R8G8B8A8 => ColorType::Rgba8,
166 RenderFormat::R32 => ColorType::Rgb32F,
167 RenderFormat::R8G8 => ColorType::La8,
168 RenderFormat::A8 => ColorType::L8,
169 RenderFormat::BC1 => ColorType::Rgba8,
170 RenderFormat::BC2 => ColorType::Rgba8,
171 RenderFormat::BC3 => ColorType::Rgba8,
172 RenderFormat::BC4 => ColorType::L8,
173 RenderFormat::BC5 => ColorType::La8,
174 RenderFormat::BC6 => ColorType::Rgba32F,
175 RenderFormat::BC7 => ColorType::Rgba8,
176 }
177 }
178
179 fn read_image(self, buf: &mut [u8]) -> ImageResult<()>
180 where
181 Self: Sized,
182 {
183 let dds = create_dds(&self.texture).map_err(|e| {
184 ImageError::IoError(std::io::Error::new(
185 std::io::ErrorKind::InvalidData,
186 format!("Failed to read the image: {}", e),
187 ))
188 })?;
189 let mut scratch_image = ScratchImage::load_dds(
190 dds.as_slice(),
191 DDS_FLAGS::DDS_FLAGS_FORCE_DX10_EXT,
192 None,
193 None,
194 )
195 .map_err(DirectXTexError)
196 .unwrap();
197
198 scratch_image = crate::convert::decompress_dds(&self.texture, scratch_image).unwrap();
199
200 if scratch_image.metadata().format == DXGI_FORMAT_R16G16B16A16_FLOAT {
201 scratch_image = scratch_image
202 .convert(
203 DXGI_FORMAT_R32G32B32A32_FLOAT,
204 TEX_FILTER_FLAGS::TEX_FILTER_DEFAULT,
205 TEX_THRESHOLD_DEFAULT,
206 )
207 .map_err(DirectXTexError)
208 .unwrap();
209 }
210
211 let blob = scratch_image
212 .image(0, 0, 0)
213 .unwrap()
214 .save_dds(DDS_FLAGS::DDS_FLAGS_FORCE_DX10_EXT)
215 .unwrap();
216
217 let data = blob.buffer();
218
219 if data.len() < buf.len() {
220 return Err(ImageError::IoError(std::io::Error::new(
221 std::io::ErrorKind::UnexpectedEof,
222 format!(
223 "DDS buffer too small: data has {} bytes, buf needs {}",
224 data.len(),
225 buf.len()
226 ),
227 )));
228 }
229
230 buf.copy_from_slice(&data[data.len() - buf.len()..]);
231
232 Ok(())
233 }
234
235 fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
236 (*self).read_image(buf)
237 }
238}
239
240pub struct BoxReflectionDecoder {
241 texture: BoxReflection,
242 layout: CubemapLayout,
243}
244
245impl BoxReflectionDecoder {
246 pub fn from_box_reflection(texture: BoxReflection, layout: CubemapLayout) -> Self {
247 Self { texture, layout }
248 }
249}
250
251impl ImageDecoder for BoxReflectionDecoder {
252 fn dimensions(&self) -> (u32, u32) {
253 let (cols, rows) = self.layout.tile_counts();
254 (
255 (BoxReflection::tile_width() * cols) as u32,
256 (BoxReflection::tile_height() * rows) as u32,
257 )
258 }
259
260 fn color_type(&self) -> ColorType {
261 ColorType::Rgba32F
262 }
263
264 fn read_image(self, buf: &mut [u8]) -> ImageResult<()>
265 where
266 Self: Sized,
267 {
268 let dds = self.texture.create_dds(Some(self.layout)).unwrap();
269 let mut scratch_image = ScratchImage::load_dds(
270 dds.as_slice(),
271 DDS_FLAGS::DDS_FLAGS_FORCE_DX10_EXT,
272 None,
273 None,
274 )
275 .map_err(DirectXTexError)
276 .unwrap();
277
278 scratch_image = scratch_image
279 .convert(
280 DXGI_FORMAT_R32G32B32A32_FLOAT,
281 TEX_FILTER_FLAGS::TEX_FILTER_DEFAULT,
282 TEX_THRESHOLD_DEFAULT,
283 )
284 .unwrap();
285
286 buf.copy_from_slice(scratch_image.pixels());
287
288 Ok(())
289 }
290
291 fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
292 (*self).read_image(buf)
293 }
294}
295
296pub(crate) mod helpers {
297 use super::*;
298 pub(super) fn color_type_to_dxgi(color_type: ExtendedColorType) -> Option<DXGI_FORMAT> {
299 match color_type {
300 ExtendedColorType::A8 => Some(DXGI_FORMAT::DXGI_FORMAT_A8_UNORM),
301 ExtendedColorType::L8 => Some(DXGI_FORMAT::DXGI_FORMAT_R8_UNORM),
302 ExtendedColorType::La8 => Some(DXGI_FORMAT::DXGI_FORMAT_R8G8_UNORM),
303 ExtendedColorType::Rgb8 => Some(DXGI_FORMAT::DXGI_FORMAT_R8G8B8A8_UNORM), ExtendedColorType::Rgba8 => Some(DXGI_FORMAT::DXGI_FORMAT_R8G8B8A8_UNORM),
305 ExtendedColorType::L16 => Some(DXGI_FORMAT::DXGI_FORMAT_R16_UNORM),
306 ExtendedColorType::La16 => Some(DXGI_FORMAT::DXGI_FORMAT_R16G16_UNORM),
307 ExtendedColorType::Rgb16 => Some(DXGI_FORMAT::DXGI_FORMAT_R16G16B16A16_UNORM), ExtendedColorType::Rgba16 => Some(DXGI_FORMAT::DXGI_FORMAT_R16G16B16A16_UNORM),
309 ExtendedColorType::Bgr8 => Some(DXGI_FORMAT::DXGI_FORMAT_B8G8R8X8_UNORM), ExtendedColorType::Bgra8 => Some(DXGI_FORMAT::DXGI_FORMAT_B8G8R8A8_UNORM),
311 ExtendedColorType::Rgb32F => Some(DXGI_FORMAT::DXGI_FORMAT_R32G32B32_FLOAT),
312 ExtendedColorType::Rgba32F => Some(DXGI_FORMAT::DXGI_FORMAT_R32G32B32A32_FLOAT),
313 _ => None,
314 }
315 }
316
317 pub(super) fn rgb8_to_rgba8(rgb: &[u8]) -> Vec<u8> {
318 let mut rgba = Vec::with_capacity(rgb.len() / 3 * 4);
319 for chunk in rgb.chunks(3) {
320 rgba.push(chunk[0]);
321 rgba.push(chunk[1]);
322 rgba.push(chunk[2]);
323 rgba.push(0xFF);
324 }
325 rgba
326 }
327
328 pub(super) fn rgb16_to_rgba16(rgb: &[u8]) -> Vec<u8> {
329 assert_eq!(rgb.len() % 6, 0, "Input length must be divisible by 6.");
330 let mut rgba = Vec::with_capacity(rgb.len() / 3 * 4);
331 for chunk in rgb.chunks(6) {
332 rgba.extend_from_slice(&chunk[0..2]);
333 rgba.extend_from_slice(&chunk[2..4]);
334 rgba.extend_from_slice(&chunk[4..6]);
335 rgba.extend_from_slice(&0xFFFFu16.to_le_bytes());
336 }
337 rgba
338 }
339
340 pub(super) fn rgb32f_to_rgba32f(rgb: &[u8]) -> Vec<u8> {
341 assert_eq!(rgb.len() % 12, 0, "Input length must be divisible by 12.");
342 let mut rgba = Vec::with_capacity(rgb.len() / 3 * 4);
343 for chunk in rgb.chunks(12) {
344 rgba.extend_from_slice(&chunk[0..4]);
345 rgba.extend_from_slice(&chunk[4..8]);
346 rgba.extend_from_slice(&chunk[8..12]);
347 rgba.extend_from_slice(&1f32.to_le_bytes());
348 }
349 rgba
350 }
351
352 pub(crate) fn dynamic_image_to_scratch_image(
353 buf: &[u8],
354 width: u32,
355 height: u32,
356 color_type: ExtendedColorType,
357 ) -> Result<ScratchImage, TextureMapEncodeError> {
358 let dxgi_format = helpers::color_type_to_dxgi(color_type)
359 .ok_or(TextureMapEncodeError::DxgiConversion(color_type))?;
360 let slice_pitch = dxgi_format
361 .compute_pitch(width as usize, height as usize, CP_FLAGS::CP_FLAGS_NONE)
362 .map_err(TextureMapEncodeError::DirectXTexError)?;
363
364 let width = width as usize;
365 let height = height as usize;
366
367 let maybe_converted;
368 let pixels = match color_type {
369 ExtendedColorType::Rgb8 | ExtendedColorType::Bgr8 => {
370 maybe_converted = Some(helpers::rgb8_to_rgba8(buf));
371 maybe_converted.as_ref().unwrap().as_ptr() as *mut u8
372 }
373 ExtendedColorType::Rgb16 => {
374 maybe_converted = Some(helpers::rgb16_to_rgba16(buf));
375 maybe_converted.as_ref().unwrap().as_ptr() as *mut u8
376 }
377 ExtendedColorType::Rgb32F => {
378 maybe_converted = Some(helpers::rgb32f_to_rgba32f(buf));
379 maybe_converted.as_ref().unwrap().as_ptr() as *mut u8
380 }
381 _ => buf.as_ptr() as *mut u8,
382 };
383
384 let image = directxtex::Image {
385 width,
386 height,
387 format: dxgi_format,
388 row_pitch: slice_pitch.row,
389 slice_pitch: slice_pitch.slice,
390 pixels,
391 };
392
393 let mut scratch_image = ScratchImage::default();
394 scratch_image.initialize_from_image(&image, false, CP_FLAGS::CP_FLAGS_NONE)?;
395 Ok(scratch_image)
396 }
397}