file.rs 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. use crate::data::Mapping;
  2. use crate::errors::{ThymeFileError, ThymeFileStructureError};
  3. use crate::image::{Image, Pixel};
  4. use serde::{Deserialize, Serialize};
  5. use std::collections::HashMap;
  6. use std::io::{Read, Seek, Write};
  7. const CURRENT_VERSION: &'static str = "0";
  8. #[derive(Debug, Clone, Copy)]
  9. pub enum StitchType {
  10. Normal,
  11. HalfUp,
  12. HalfDown,
  13. }
  14. impl StitchType {
  15. fn to_u8(&self) -> u8 {
  16. match self {
  17. StitchType::Normal => 'x' as u8,
  18. StitchType::HalfUp => '/' as u8,
  19. StitchType::HalfDown => '\\' as u8,
  20. }
  21. }
  22. }
  23. #[derive(Debug, Clone)]
  24. pub struct Stitch {
  25. pub color: ColorIdx,
  26. pub typ: StitchType,
  27. }
  28. #[derive(Debug, Serialize, Deserialize)]
  29. pub struct Color {
  30. pub name: String,
  31. pub symbol: String,
  32. pub color: Pixel,
  33. }
  34. #[derive(Debug, Clone, Copy)]
  35. pub struct ColorIdx {
  36. pub idx: u32,
  37. }
  38. #[derive(Debug)]
  39. pub struct ThymeFile {
  40. pub width: u32,
  41. pub height: u32,
  42. pub palette: Vec<Color>,
  43. pub payload: Vec<Option<Stitch>>,
  44. pub metadata: HashMap<String, String>,
  45. }
  46. impl ThymeFile {
  47. pub fn from_image_and_config(image: &Image, Mapping(mapping): &Mapping) -> ThymeFile {
  48. let width = image.width;
  49. let height = image.height;
  50. let mut palette = Vec::new();
  51. let mut lookup = HashMap::new();
  52. let mut next_idx = 0;
  53. for (color, info) in mapping.iter() {
  54. if let Some(i) = info {
  55. let idx = ColorIdx { idx: next_idx };
  56. next_idx += 1;
  57. palette.push(Color {
  58. color: *color,
  59. name: i.name.to_owned(),
  60. symbol: i.symbol.to_owned(),
  61. });
  62. lookup.insert(color, idx);
  63. }
  64. }
  65. let payload = image
  66. .iter()
  67. .map(|(_idx, pixel)| {
  68. if let Some(color) = lookup.get(&pixel) {
  69. Some(Stitch {
  70. color: *color,
  71. typ: StitchType::Normal,
  72. })
  73. } else {
  74. None
  75. }
  76. })
  77. .collect();
  78. let metadata = HashMap::new();
  79. ThymeFile {
  80. width,
  81. height,
  82. palette,
  83. payload,
  84. metadata,
  85. }
  86. }
  87. pub fn blank(width: u32, height: u32) -> ThymeFile {
  88. let palette = Vec::new();
  89. let mut payload = Vec::with_capacity((width * height) as usize);
  90. for _ in 0..(width * height) {
  91. payload.push(None);
  92. }
  93. let metadata = HashMap::new();
  94. ThymeFile {
  95. width,
  96. height,
  97. palette,
  98. payload,
  99. metadata,
  100. }
  101. }
  102. pub fn from_stream<R>(stream: &mut R) -> Result<ThymeFile, ThymeFileError>
  103. where
  104. R: Read + Seek,
  105. {
  106. fn missing_component(component: &str) -> ThymeFileError {
  107. ThymeFileStructureError::new(format!(
  108. "Missing component in thyme file: `{}`",
  109. component
  110. ))
  111. .into()
  112. }
  113. let mut zip = zip::ZipArchive::new(stream)?;
  114. let mut width = None;
  115. let mut height = None;
  116. let mut metadata = None;
  117. let mut palette = None;
  118. let mut payload = None;
  119. for i in 0..zip.len() {
  120. let file = zip.by_index(i)?;
  121. match file.name() {
  122. "THYME_VERSION" => {
  123. // TODO: actually check this
  124. }
  125. "dimensions.json" => {
  126. let (w, h): (u32, u32) = serde_json::from_reader(file)?;
  127. width = Some(w);
  128. height = Some(h);
  129. }
  130. "metadata.json" => metadata = Some(serde_json::from_reader(file)?),
  131. "palette.json" => palette = Some(serde_json::from_reader(file)?),
  132. "payload.json" => {
  133. let ps: Vec<Option<IntermediateStitch>> = serde_json::from_reader(file)?;
  134. let ps: Result<Vec<Option<Stitch>>, ThymeFileError> = ps
  135. .iter()
  136. .map(|n| n.map(IntermediateStitch::to_stitch).transpose())
  137. .collect();
  138. payload = Some(ps?);
  139. }
  140. name => {
  141. return Err(ThymeFileStructureError::new(format!(
  142. "Unrecognized element: {}",
  143. name
  144. ))
  145. .into())
  146. }
  147. }
  148. // whatever
  149. }
  150. Ok(ThymeFile {
  151. width: width.ok_or_else(|| missing_component("dimensions"))?,
  152. height: height.ok_or_else(|| missing_component("dimensions"))?,
  153. palette: palette.ok_or_else(|| missing_component("palette"))?,
  154. payload: payload.ok_or_else(|| missing_component("payload"))?,
  155. metadata: metadata.ok_or_else(|| missing_component("metadata"))?,
  156. })
  157. }
  158. pub fn to_stream<W>(&self, stream: &mut W) -> std::io::Result<()>
  159. where
  160. W: Write + Seek,
  161. {
  162. let mut zip = zip::ZipWriter::new(stream);
  163. let options =
  164. zip::write::FileOptions::default().compression_method(zip::CompressionMethod::Bzip2);
  165. // add version metadata
  166. zip.start_file("THYME_VERSION", options)?;
  167. writeln!(zip, "{}", CURRENT_VERSION)?;
  168. // add dimensions
  169. zip.start_file("dimensions.json", options)?;
  170. serde_json::to_writer(&mut zip, &(self.width, self.height))?;
  171. // add metadata
  172. zip.start_file("metadata.json", options)?;
  173. serde_json::to_writer(&mut zip, &self.metadata)?;
  174. // add palette
  175. zip.start_file("palette.json", options)?;
  176. serde_json::to_writer(&mut zip, &self.palette)?;
  177. // add image payload
  178. zip.start_file("payload.json", options)?;
  179. serde_json::to_writer(&mut zip, &self.json_stitches())?;
  180. // add version
  181. zip.finish()?;
  182. Ok(())
  183. }
  184. fn json_stitches(&self) -> Vec<Option<IntermediateStitch>> {
  185. self.payload
  186. .iter()
  187. .map(|stitch| match stitch {
  188. Some(s) => Some(IntermediateStitch(s.typ.to_u8(), s.color.idx)),
  189. None => None,
  190. })
  191. .collect()
  192. }
  193. pub fn iter<'a>(&'a self) -> ThymeImageIterator<'a> {
  194. ThymeImageIterator {
  195. palette: &self.palette,
  196. x: 0,
  197. y: 0,
  198. width: self.width,
  199. values: self.payload.iter(),
  200. }
  201. }
  202. }
  203. // internal structs for serializing/deserializing the image part
  204. #[derive(Debug, Clone, Copy, Serialize, Deserialize)]
  205. struct IntermediateStitch(u8, u32);
  206. impl IntermediateStitch {
  207. fn to_stitch(self) -> Result<Stitch, ThymeFileError> {
  208. let typ = match self.0 {
  209. b'x' => StitchType::Normal,
  210. b'/' => StitchType::HalfUp,
  211. b'\\' => StitchType::HalfDown,
  212. _ => {
  213. return Err(ThymeFileStructureError::new(format!(
  214. "Unknown stitch type: {}",
  215. self.0
  216. ))
  217. .into())
  218. }
  219. };
  220. Ok(Stitch {
  221. typ,
  222. color: ColorIdx { idx: self.1 },
  223. })
  224. }
  225. }
  226. pub struct ThymeImageIterator<'a> {
  227. width: u32,
  228. x: u32,
  229. y: u32,
  230. palette: &'a [Color],
  231. values: std::slice::Iter<'a, Option<Stitch>>,
  232. }
  233. impl<'a> Iterator for ThymeImageIterator<'a> {
  234. type Item = ((u32, u32), Option<(StitchType, &'a Color)>);
  235. fn next(&mut self) -> Option<Self::Item> {
  236. let px = self.values.next();
  237. if let Some(px) = px {
  238. let r = px.as_ref().map(|stitch| {
  239. (stitch.typ, &self.palette[stitch.color.idx as usize])
  240. });
  241. let ret = ((self.x, self.y), r);
  242. self.x += 1;
  243. if self.x == self.width {
  244. self.x = 0;
  245. self.y += 1;
  246. }
  247. Some(ret)
  248. } else {
  249. None
  250. }
  251. }
  252. }