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), 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), ExtendedColorType::Rgba16 => Some(DXGI_FORMAT::DXGI_FORMAT_R16G16B16A16_UNORM),
232 ExtendedColorType::Bgr8 => Some(DXGI_FORMAT::DXGI_FORMAT_B8G8R8X8_UNORM), 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}