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