unity_asset_decode/texture/helpers/
export.rs1use crate::error::{BinaryError, Result};
6use image::{ImageFormat, RgbaImage};
7use std::path::Path;
8
9pub struct TextureExporter;
13
14impl TextureExporter {
15 pub fn export_png<P: AsRef<Path>>(image: &RgbaImage, path: P) -> Result<()> {
20 image
21 .save_with_format(path, ImageFormat::Png)
22 .map_err(|e| BinaryError::generic(format!("Failed to save PNG: {}", e)))
23 }
24
25 pub fn export_jpeg<P: AsRef<Path>>(image: &RgbaImage, path: P, quality: u8) -> Result<()> {
29 let rgb_image = image::DynamicImage::ImageRgba8(image.clone()).to_rgb8();
31
32 let mut output = std::fs::File::create(path)
33 .map_err(|e| BinaryError::generic(format!("Failed to create output file: {}", e)))?;
34
35 let mut encoder = image::codecs::jpeg::JpegEncoder::new_with_quality(&mut output, quality);
36 encoder
37 .encode_image(&rgb_image)
38 .map_err(|e| BinaryError::generic(format!("Failed to encode JPEG: {}", e)))
39 }
40
41 pub fn export_bmp<P: AsRef<Path>>(image: &RgbaImage, path: P) -> Result<()> {
43 image
44 .save_with_format(path, ImageFormat::Bmp)
45 .map_err(|e| BinaryError::generic(format!("Failed to save BMP: {}", e)))
46 }
47
48 pub fn export_tiff<P: AsRef<Path>>(image: &RgbaImage, path: P) -> Result<()> {
50 image
51 .save_with_format(path, ImageFormat::Tiff)
52 .map_err(|e| BinaryError::generic(format!("Failed to save TIFF: {}", e)))
53 }
54
55 pub fn export_auto<P: AsRef<Path>>(image: &RgbaImage, path: P) -> Result<()> {
57 let path_ref = path.as_ref();
58 let extension = path_ref
59 .extension()
60 .and_then(|ext| ext.to_str())
61 .unwrap_or("")
62 .to_lowercase();
63
64 match extension.as_str() {
65 "png" => Self::export_png(image, path),
66 "jpg" | "jpeg" => Self::export_jpeg(image, path, 90), "bmp" => Self::export_bmp(image, path),
68 "tif" | "tiff" => Self::export_tiff(image, path),
69 _ => {
70 Self::export_png(image, path)
72 }
73 }
74 }
75
76 pub fn export_with_format<P: AsRef<Path>>(
78 image: &RgbaImage,
79 path: P,
80 format: ImageFormat,
81 ) -> Result<()> {
82 image.save_with_format(path, format).map_err(|e| {
83 BinaryError::generic(format!(
84 "Failed to save image with format {:?}: {}",
85 format, e
86 ))
87 })
88 }
89
90 pub fn supported_formats() -> Vec<&'static str> {
92 vec!["png", "jpg", "jpeg", "bmp", "tiff", "tif"]
93 }
94
95 pub fn is_format_supported(extension: &str) -> bool {
97 Self::supported_formats().contains(&extension.to_lowercase().as_str())
98 }
99
100 pub fn create_filename(base_name: &str, format: &str) -> String {
102 let clean_base =
103 base_name.trim_end_matches(|c: char| !c.is_alphanumeric() && c != '_' && c != '-');
104 format!("{}.{}", clean_base, format.to_lowercase())
105 }
106
107 pub fn validate_for_export(image: &RgbaImage) -> Result<()> {
109 let (width, height) = image.dimensions();
110
111 if width == 0 || height == 0 {
112 return Err(BinaryError::invalid_data("Image has zero dimensions"));
113 }
114
115 if width > 32768 || height > 32768 {
117 return Err(BinaryError::invalid_data(
118 "Image dimensions too large for export",
119 ));
120 }
121
122 Ok(())
123 }
124
125 pub fn export_validated<P: AsRef<Path>>(image: &RgbaImage, path: P) -> Result<()> {
127 Self::validate_for_export(image)?;
128 Self::export_auto(image, path)
129 }
130}
131
132#[derive(Debug, Clone)]
134pub struct ExportOptions {
135 pub format: ImageFormat,
136 pub quality: Option<u8>, pub compression: Option<u8>, }
139
140impl Default for ExportOptions {
141 fn default() -> Self {
142 Self {
143 format: ImageFormat::Png,
144 quality: Some(90),
145 compression: Some(6),
146 }
147 }
148}
149
150impl ExportOptions {
151 pub fn png() -> Self {
153 Self {
154 format: ImageFormat::Png,
155 quality: None,
156 compression: Some(6),
157 }
158 }
159
160 pub fn jpeg(quality: u8) -> Self {
162 Self {
163 format: ImageFormat::Jpeg,
164 quality: Some(quality.clamp(1, 100)),
165 compression: None,
166 }
167 }
168
169 pub fn bmp() -> Self {
171 Self {
172 format: ImageFormat::Bmp,
173 quality: None,
174 compression: None,
175 }
176 }
177
178 pub fn export<P: AsRef<Path>>(&self, image: &RgbaImage, path: P) -> Result<()> {
180 match self.format {
181 ImageFormat::Jpeg => {
182 let quality = self.quality.unwrap_or(90);
183 TextureExporter::export_jpeg(image, path, quality)
184 }
185 _ => TextureExporter::export_with_format(image, path, self.format),
186 }
187 }
188}