teki_common/utils/
sprite_sheet.rs1use regex::Regex;
2use serde_json::Value;
3use std::collections::HashMap;
4use std::rc::Rc;
5
6#[derive(Default)]
7pub struct SpriteSheet {
8 sprite_sheets: Vec<Rc<SpriteSheet1>>,
9 sheet_map: HashMap<String, Rc<SpriteSheet1>>,
10}
11
12impl SpriteSheet {
13 pub fn load_sprite_sheet(&mut self, text: &str) -> bool {
14 if let Some(sprite_sheet) = SpriteSheet1::load(text) {
15 let sprite_sheet = Rc::new(sprite_sheet);
16 self.sprite_sheets.push(sprite_sheet.clone());
17 for (key, _sheet) in sprite_sheet.as_ref().sheets.iter() {
18 self.sheet_map.insert(key.clone(), sprite_sheet.clone());
19 }
20 true
21 } else {
22 false
23 }
24 }
25
26 pub fn get(&self, key: &str) -> Option<(&Sheet, &str)> {
27 self.sheet_map
28 .get(key)
29 .map(|ss| ss.get(key).map(|sheet| (sheet, ss.texture_name.as_str())))
30 .flatten()
31 }
32}
33
34#[derive(Clone)]
35struct SpriteSheet1 {
36 texture_name: String,
37 sheets: HashMap<String, Sheet>,
38}
39
40#[derive(Clone)]
41pub struct Sheet {
42 pub frame: Rect,
43 pub rotated: bool,
44 pub trimmed: Option<Trimmed>,
45}
46
47#[derive(Clone)]
48pub struct Rect {
49 pub x: i32,
50 pub y: i32,
51 pub w: u32,
52 pub h: u32,
53}
54
55#[derive(Clone)]
56pub struct Size {
57 pub w: u32,
58 pub h: u32,
59}
60
61#[derive(Clone)]
62pub struct Trimmed {
63 pub sprite_source_size: Rect,
64 pub source_size: Size,
65}
66
67impl SpriteSheet1 {
68 pub fn load(text: &str) -> Option<Self> {
69 let deserialized_opt = serde_json::from_str(text);
70 if let Err(_err) = deserialized_opt {
71 return None;
72 }
73 let deserialized: Value = deserialized_opt.unwrap();
74
75 let texture_name = get_mainname(deserialized["meta"]["image"].as_str()?);
76
77 let mut sheets = HashMap::new();
78 for (key, frame) in deserialized["frames"].as_object()? {
79 let sheet = convert_sheet(frame)?;
80 sheets.insert(get_mainname(key), sheet);
81 }
82 Some(Self { texture_name, sheets })
83 }
84
85 pub fn get(&self, key: &str) -> Option<&Sheet> {
86 self.sheets.get(key)
87 }
88}
89
90fn convert_sheet(sheet: &Value) -> Option<Sheet> {
91 let frame = convert_rect(&sheet["frame"])?;
92 let rotated = sheet["rotated"].as_bool()?;
93 let trimmed = if sheet["trimmed"].as_bool() == Some(true) {
94 let sprite_source_size = convert_rect(&sheet["spriteSourceSize"])?;
95 let source_size = convert_size(&sheet["sourceSize"])?;
96 Some(Trimmed { sprite_source_size, source_size })
97 } else {
98 None
99 };
100
101 Some(Sheet { frame, rotated, trimmed })
102}
103
104fn convert_rect(value: &Value) -> Option<Rect> {
105 Some(Rect {
106 x: value["x"].as_i64()? as i32,
107 y: value["y"].as_i64()? as i32,
108 w: value["w"].as_i64()? as u32,
109 h: value["h"].as_i64()? as u32,
110 })
111}
112
113fn convert_size(value: &Value) -> Option<Size> {
114 Some(Size { w: value["w"].as_i64()? as u32, h: value["h"].as_i64()? as u32 })
115}
116
117fn get_mainname(filename: &str) -> String {
118 let re = Regex::new(r"^(.*)\.\w+").unwrap();
119 re.captures(filename)
120 .map_or_else(|| filename.to_string(), |caps| caps.get(1).unwrap().as_str().to_string())
121}