unity_asset_binary/sprite/
mod.rs

1//! Unity Sprite processing module
2//!
3//! This module provides comprehensive Sprite processing capabilities,
4//! organized following UnityPy and unity-rs best practices.
5//!
6//! # Architecture
7//!
8//! The module is organized into several sub-modules:
9//! - `types` - Core data structures (Sprite, SpriteRenderData, etc.)
10//! - `parser` - Sprite parsing from Unity objects
11//! - `processor` - High-level sprite processing and image extraction
12//!
13//! # Examples
14//!
15//! ```rust,no_run
16//! use unity_asset_binary::sprite::{SpriteProcessor, SpriteConfig};
17//! use unity_asset_binary::unity_version::UnityVersion;
18//! use unity_asset_binary::object::ObjectInfo;
19//!
20//! // Create processor with custom configuration
21//! let version = UnityVersion::parse_version("2020.3.12f1")?;
22//! let config = SpriteConfig {
23//!     extract_images: true,
24//!     process_atlas: true,
25//!     max_sprite_size: Some((2048, 2048)),
26//!     apply_transformations: true,
27//! };
28//! let processor = SpriteProcessor::with_config(version, config);
29//!
30//! // Note: In real usage, you would create a UnityObject from parsed data
31//! // For demonstration, we'll just show the processor creation
32//! println!("Sprite processed successfully");
33//! # Ok::<(), unity_asset_binary::error::BinaryError>(())
34//! ```
35
36pub mod parser;
37pub mod processor;
38pub mod types;
39
40// Re-export main types for easy access
41pub use parser::SpriteParser;
42pub use processor::{SpriteProcessor, SpriteStats};
43pub use types::{
44    // Core sprite types
45    Sprite,
46    SpriteAtlas,
47    SpriteBorder,
48    // Configuration and results
49    SpriteConfig,
50    SpriteInfo,
51    SpriteOffset,
52    SpritePivot,
53    SpriteRect,
54    SpriteRenderData,
55    SpriteResult,
56    SpriteSettings,
57};
58
59/// Main sprite processing facade
60///
61/// This struct provides a high-level interface for sprite processing,
62/// combining parsing and processing functionality.
63pub struct SpriteManager {
64    processor: SpriteProcessor,
65}
66
67impl SpriteManager {
68    /// Create a new sprite manager
69    pub fn new(version: crate::unity_version::UnityVersion) -> Self {
70        Self {
71            processor: SpriteProcessor::new(version),
72        }
73    }
74
75    /// Create a sprite manager with custom configuration
76    pub fn with_config(version: crate::unity_version::UnityVersion, config: SpriteConfig) -> Self {
77        Self {
78            processor: SpriteProcessor::with_config(version, config),
79        }
80    }
81
82    /// Process sprite from Unity object
83    pub fn process_sprite(
84        &self,
85        object: &crate::object::UnityObject,
86    ) -> crate::error::Result<SpriteResult> {
87        self.processor.parse_sprite(object)
88    }
89
90    /// Process sprite with texture for image extraction
91    pub fn process_sprite_with_texture(
92        &self,
93        sprite_object: &crate::object::UnityObject,
94        texture: &crate::texture::Texture2D,
95    ) -> crate::error::Result<SpriteResult> {
96        self.processor
97            .process_sprite_with_texture(sprite_object, texture)
98    }
99
100    /// Process multiple sprites as an atlas
101    pub fn process_sprite_atlas(
102        &self,
103        sprites: &[&crate::object::UnityObject],
104    ) -> crate::error::Result<SpriteAtlas> {
105        self.processor.process_sprite_atlas(sprites)
106    }
107
108    /// Get sprite statistics
109    pub fn get_statistics(&self, sprites: &[&Sprite]) -> SpriteStats {
110        self.processor.get_sprite_stats(sprites)
111    }
112
113    /// Validate sprite data
114    pub fn validate_sprite(&self, sprite: &Sprite) -> crate::error::Result<()> {
115        self.processor.validate_sprite(sprite)
116    }
117
118    /// Get supported features
119    pub fn get_supported_features(&self) -> Vec<&'static str> {
120        self.processor.get_supported_features()
121    }
122
123    /// Check if a feature is supported
124    pub fn is_feature_supported(&self, feature: &str) -> bool {
125        self.processor.is_feature_supported(feature)
126    }
127
128    /// Get the current configuration
129    pub fn config(&self) -> &SpriteConfig {
130        self.processor.config()
131    }
132
133    /// Set the configuration
134    pub fn set_config(&mut self, config: SpriteConfig) {
135        self.processor.set_config(config);
136    }
137
138    /// Get the Unity version
139    pub fn version(&self) -> &crate::unity_version::UnityVersion {
140        self.processor.version()
141    }
142
143    /// Set the Unity version
144    pub fn set_version(&mut self, version: crate::unity_version::UnityVersion) {
145        self.processor.set_version(version);
146    }
147}
148
149impl Default for SpriteManager {
150    fn default() -> Self {
151        Self::new(crate::unity_version::UnityVersion::default())
152    }
153}
154
155/// Convenience functions for common operations
156/// Create a sprite manager with default settings
157pub fn create_manager(version: crate::unity_version::UnityVersion) -> SpriteManager {
158    SpriteManager::new(version)
159}
160
161/// Create a sprite manager optimized for performance
162pub fn create_performance_manager(version: crate::unity_version::UnityVersion) -> SpriteManager {
163    let config = SpriteConfig {
164        extract_images: false,
165        process_atlas: false,
166        max_sprite_size: Some((1024, 1024)),
167        apply_transformations: false,
168    };
169    SpriteManager::with_config(version, config)
170}
171
172/// Create a sprite manager with full features
173pub fn create_full_manager(version: crate::unity_version::UnityVersion) -> SpriteManager {
174    let config = SpriteConfig {
175        extract_images: true,
176        process_atlas: true,
177        max_sprite_size: None,
178        apply_transformations: true,
179    };
180    SpriteManager::with_config(version, config)
181}
182
183/// Parse sprite from Unity object (convenience function)
184pub fn parse_sprite(
185    object: &crate::object::UnityObject,
186    version: &crate::unity_version::UnityVersion,
187) -> crate::error::Result<Sprite> {
188    let parser = SpriteParser::new(version.clone());
189    let result = parser.parse_from_unity_object(object)?;
190    Ok(result.sprite)
191}
192
193/// Extract sprite image from texture (convenience function)
194pub fn extract_sprite_image(
195    sprite: &Sprite,
196    texture: &crate::texture::Texture2D,
197    version: &crate::unity_version::UnityVersion,
198) -> crate::error::Result<Vec<u8>> {
199    let processor = SpriteProcessor::new(version.clone());
200    processor.extract_sprite_image(sprite, texture)
201}
202
203/// Validate sprite data (convenience function)
204pub fn validate_sprite(sprite: &Sprite) -> crate::error::Result<()> {
205    let processor = SpriteProcessor::default();
206    processor.validate_sprite(sprite)
207}
208
209/// Get sprite area in pixels
210pub fn get_sprite_area(sprite: &Sprite) -> f32 {
211    sprite.get_area()
212}
213
214/// Check if sprite is 9-slice
215pub fn is_nine_slice_sprite(sprite: &Sprite) -> bool {
216    sprite.has_border()
217}
218
219/// Check if sprite is from atlas
220pub fn is_atlas_sprite(sprite: &Sprite) -> bool {
221    sprite.is_atlas_sprite()
222}
223
224/// Get sprite aspect ratio
225pub fn get_sprite_aspect_ratio(sprite: &Sprite) -> f32 {
226    sprite.get_aspect_ratio()
227}
228
229/// Check if Unity version supports sprite feature
230pub fn is_sprite_feature_supported(
231    version: &crate::unity_version::UnityVersion,
232    feature: &str,
233) -> bool {
234    match feature {
235        "basic_sprite" | "rect" | "pivot" => true,
236        "border" | "pixels_to_units" => version.major >= 5,
237        "polygon_sprites" | "sprite_atlas" => version.major >= 2017,
238        "sprite_mesh" | "sprite_physics" => version.major >= 2018,
239        _ => false,
240    }
241}
242
243/// Get recommended sprite configuration for Unity version
244pub fn get_recommended_config(version: &crate::unity_version::UnityVersion) -> SpriteConfig {
245    if version.major >= 2018 {
246        // Modern Unity - full features
247        SpriteConfig {
248            extract_images: true,
249            process_atlas: true,
250            max_sprite_size: None,
251            apply_transformations: true,
252        }
253    } else if version.major >= 2017 {
254        // Unity 2017 - atlas support
255        SpriteConfig {
256            extract_images: true,
257            process_atlas: true,
258            max_sprite_size: Some((2048, 2048)),
259            apply_transformations: true,
260        }
261    } else if version.major >= 5 {
262        // Unity 5+ - basic features
263        SpriteConfig {
264            extract_images: true,
265            process_atlas: false,
266            max_sprite_size: Some((1024, 1024)),
267            apply_transformations: false,
268        }
269    } else {
270        // Legacy Unity - minimal features
271        SpriteConfig {
272            extract_images: false,
273            process_atlas: false,
274            max_sprite_size: Some((512, 512)),
275            apply_transformations: false,
276        }
277    }
278}
279
280/// Sprite processing options
281#[derive(Debug, Clone)]
282pub struct ProcessingOptions {
283    pub parallel_processing: bool,
284    pub cache_results: bool,
285    pub validate_sprites: bool,
286    pub generate_thumbnails: bool,
287}
288
289impl Default for ProcessingOptions {
290    fn default() -> Self {
291        Self {
292            parallel_processing: false,
293            cache_results: true,
294            validate_sprites: true,
295            generate_thumbnails: false,
296        }
297    }
298}
299
300#[cfg(test)]
301mod tests {
302    use super::*;
303
304    #[test]
305    fn test_manager_creation() {
306        let version = crate::unity_version::UnityVersion::default();
307        let manager = create_manager(version);
308        assert!(manager.get_supported_features().contains(&"basic_sprite"));
309    }
310
311    #[test]
312    fn test_performance_manager() {
313        let version = crate::unity_version::UnityVersion::default();
314        let manager = create_performance_manager(version);
315        assert!(!manager.config().extract_images);
316        assert!(!manager.config().process_atlas);
317    }
318
319    #[test]
320    fn test_full_manager() {
321        let version = crate::unity_version::UnityVersion::default();
322        let manager = create_full_manager(version);
323        assert!(manager.config().extract_images);
324        assert!(manager.config().process_atlas);
325    }
326
327    #[test]
328    fn test_feature_support() {
329        let version_2020 =
330            crate::unity_version::UnityVersion::parse_version("2020.3.12f1").unwrap();
331        assert!(is_sprite_feature_supported(&version_2020, "basic_sprite"));
332        assert!(is_sprite_feature_supported(
333            &version_2020,
334            "polygon_sprites"
335        ));
336        assert!(is_sprite_feature_supported(&version_2020, "sprite_mesh"));
337
338        let version_2017 =
339            crate::unity_version::UnityVersion::parse_version("2017.4.40f1").unwrap();
340        assert!(is_sprite_feature_supported(&version_2017, "sprite_atlas"));
341        assert!(!is_sprite_feature_supported(&version_2017, "sprite_mesh"));
342    }
343
344    #[test]
345    fn test_recommended_config() {
346        let version_2020 =
347            crate::unity_version::UnityVersion::parse_version("2020.3.12f1").unwrap();
348        let config = get_recommended_config(&version_2020);
349        assert!(config.extract_images);
350        assert!(config.process_atlas);
351        assert!(config.apply_transformations);
352
353        let version_5 = crate::unity_version::UnityVersion::parse_version("5.6.7f1").unwrap();
354        let config = get_recommended_config(&version_5);
355        assert!(config.extract_images);
356        assert!(!config.process_atlas);
357    }
358}