1use std::collections::HashMap;
2use fronma::{engines::Toml, parser::parse_with_engine};
3use serde::Deserialize;
4use regex::{Regex, Match};
5use itertools::Itertools;
6use wide::f64x4;
7use wasm_bindgen::prelude::*;
8use crc32fast::hash;
9use deflate::{deflate_bytes_zlib_conf, Compression};
10
11#[derive(Deserialize, Clone)]
12struct Config {
13 size: SizeConfig,
14 colors: HashMap<char, String>,
15 options: Option<Options>,
16 meta: Option<HashMap<String, String>>
17}
18
19#[derive(Deserialize, Clone)]
20struct SizeConfig {
21 w: usize,
22 h: usize,
23 scale: usize,
24 frames: usize,
25 rate: Option<[u16; 2]>
26}
27
28#[derive(Deserialize, Clone)]
29struct Options {
30 order: Option<Vec<usize>>
31}
32
33struct Layer {
34 index: usize,
35 content: LayerContent
36}
37
38struct Frame {
39 index: usize,
40 content: Vec<String>
41}
42
43enum LayerContent {
44 Still(Vec<String>),
45 Video(Vec<Frame>)
46}
47
48enum LayerPixmap { Still(Vec<Vec<(u8, u8, u8, u8)>>),
50 Video(Frames),
51}
52
53#[derive(PartialEq)]
54enum Token {
55 Layer(usize),
56 Frame(usize),
57 Normal(String)
58}
59
60type Frames = Vec<Vec<Vec<(u8, u8, u8, u8)>>>;
61
62#[wasm_bindgen]
63pub fn compile(s: &str) -> Result<Vec<u8>, String> {
64 let data = parse_with_engine::<Config, Toml>(s).unwrap();
65 let config = data.headers;
66 let body = data.body;
67 println!("[1/5] Parsing...");
68 let token = body.lines().map(|c| tokenize(c)).filter(|c| c != &Token::Normal("".to_string())).collect::<Vec<_>>();
69 let ast = parse(token);
70 println!("[2/5] Putting color data...");
71 let layers = generate_layers(&config.clone(), ast);
72 println!("[3/5] Merging layers...");
73 let frames = generate_frames(&config, layers);
74 println!("[4/5] Applying options...");
75 let applyed = if let Some(option) = config.clone().options {
76 applyoption(frames, option)
77 } else {
78 frames
79 };
80 println!("[5/5] Generating (A)PNG...");
81 let result = generate_image(applyed, &config);
82 result
83}
84
85fn generate_image(frames_r: Frames, conf: &Config) -> Result<Vec<u8>, String> {
86 let frames = &frames_r;
87 let mut result: Vec<u8> = vec![];
88 if frames.is_empty() {
89 return Err("Image is empty".to_string());
90 }
91 if frames.len() != conf.size.frames {
92 return Err("Frame counts does not match.".to_string());
93 }
94 let heights = frames.iter().map(|c| c.len());
95 let widths = frames.iter().map(|c| c.iter().map(|d| d.len()).collect::<Vec<_>>()).concat();
96 if heights.clone().min().unwrap() == 0 {
97 return Err("Height is zero".to_string());
98 }
99 if widths.iter().min().unwrap() == &0 {
100 return Err("Width is zero".to_string());
101 }
102 if heights.clone().min().unwrap() != heights.max().unwrap() {
103 return Err("Unaligned heights.".to_string());
104 }
105 if widths.iter().min().unwrap() != widths.iter().max().unwrap() {
106 return Err("Unaligned widths.".to_string());
107 }
108 result.extend([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
109 { let mut ihdr: Vec<u8> = vec![];
111 ihdr.extend(b"IHDR");
112 ihdr.extend(((conf.size.w * conf.size.scale) as u32).to_be_bytes());
113 ihdr.extend(((conf.size.h * conf.size.scale) as u32).to_be_bytes());
114 ihdr.extend([0x08, 0x06, 0x00, 0x00, 0x00]);
115 result.extend(write_chunk(&ihdr));
116 }
117 result.extend(write_chunk(b"tEXtGenerator\x00Pixdown")); if let Some(meta) = conf.meta.clone() {
119 meta.iter().for_each(|(k, v)| {
120 let mut itxt: Vec<u8> = vec![];
121 itxt.extend(b"iTXt");
122 itxt.extend(k.as_bytes());
123 itxt.extend([0x00, 0x01, 0x00, 0x00, 0x00]); itxt.extend(deflate_bytes_zlib_conf(&v.as_bytes(), Compression::Best));
125 result.extend(write_chunk(&itxt));
126 });
127 }
128 if conf.size.frames > 1 { let mut actl: Vec<u8> = vec![];
130 actl.extend(b"acTL");
131 actl.extend((conf.size.frames as u32).to_be_bytes());
132 actl.extend([0x00, 0x00, 0x00, 0x00]);
133 result.extend(write_chunk(&actl));
134 }
135 let mut sequence = 0u32;
136 frames.iter().enumerate().for_each(|(f, fd)| {
137 if conf.size.frames > 1 { let mut fctl: Vec<u8> = vec![];
139 fctl.extend(b"fcTL");
140 fctl.extend(sequence.to_be_bytes());
141 sequence += 1;
142 fctl.extend(((conf.size.w * conf.size.scale) as u32).to_be_bytes());
143 fctl.extend(((conf.size.h * conf.size.scale) as u32).to_be_bytes());
144 fctl.extend(0u32.to_be_bytes());
145 fctl.extend(0u32.to_be_bytes());
146 let rate = conf.size.rate.unwrap_or([1, 24]);
147 fctl.extend(rate[0].to_be_bytes());
148 fctl.extend(if rate[1] == 0 { 24 } else {rate[1] }.to_be_bytes());
149 fctl.extend([0x00u8, 0x01u8]);
150 result.extend(write_chunk(&fctl));
151 }
152 {
153 let mut fdat: Vec<u8> = vec![]; {
155 let mut pixmap: Vec<u8> = vec![];
156 if f == 0 {
157 fdat.extend(b"IDAT");
158 } else {
159 fdat.extend(b"fdAT");
160 fdat.extend(sequence.to_be_bytes());
161 sequence += 1;
162 }
163 fd.iter().for_each(|yd| {
164 for _ in 0..conf.size.scale {
165 pixmap.push(0);
166 yd.iter().for_each(|xd| {
167 for _ in 0..conf.size.scale {
168 pixmap.extend([xd.0, xd.1, xd.2, xd.3]);
169 }
170 });
171 }
172 });
173 fdat.extend(deflate_bytes_zlib_conf(&pixmap, Compression::Best));
174 }
175 result.extend(write_chunk(&fdat));
176 }
177 println!("Generated frame {}", f);
178 });
179 result.extend(write_chunk(b"IEND"));
180 Ok(result)
181}
182
183fn write_chunk(data: &[u8]) -> Vec<u8> {
184 let mut result: Vec<u8> = vec![];
185 result.extend((data.len() as u32 - 4).to_be_bytes());
186 result.extend(data);
187 result.extend(hash(data).to_be_bytes());
188 result
189}
190
191fn applyoption(frames: Frames, option: Options) -> Frames {
192 let mut result: Frames = frames;
193 if let Some(args) = option.order {
194 result = options::order(result, args);
195 }
196 result
197}
198
199fn generate_frames(conf: &Config, layers: Vec<LayerPixmap>) -> Frames {
200 let mut frames: Frames = vec![];
201 let mix = |lf: (u8, u8, u8, u8), lb: (f64, f64, f64, f64)| -> (f64, f64, f64, f64) {
202 let cf = f64x4::from([lf.0 as f64, lf.1 as f64, lf.2 as f64, 0.0f64]);
203 let cb = f64x4::from([lb.0, lb.1, lb.2, 0.0f64]);
204 let af = f64x4::splat(lf.3 as f64 / 255.0);
205 let ab = f64x4::splat(lb.3 / 255.0);
206 let one = f64x4::splat(1.0);
207 let c_f = cf * af;
208 let c_b = cb * ab;
209 let a = af + ab * (one - af);
210 let c_ = c_f + c_b * (one - af);
211 let c = if a == f64x4::splat(0.0) {
212 f64x4::splat(0.0)
213 } else {
214 (c_ / a).max(f64x4::splat(0.0)).min(f64x4::splat(255.0))
215 }.to_array();
216 (c[0], c[1], c[2], a.to_array()[0] * 255.0)
217 };
218 for f in 0..conf.size.frames {
219 let mut frame = vec![vec![(0.0f64, 0.0f64, 0.0f64, 0.0f64); conf.size.w];conf.size.h];
220 for l in layers.iter() {
221 if let LayerPixmap::Still(v) = l {
222 v.iter().enumerate().for_each(|(y, c)| {
223 c.iter().enumerate().for_each(|(x, &d)| {
224 let b = frame[y % conf.size.h][x % conf.size.w];
225 frame[y % conf.size.h][x % conf.size.w] = mix(d, b);
226 });
227 });
228 }
229 if let LayerPixmap::Video(vs) = l {
230 let v = vs[f % vs.len()].clone();
231 v.iter().enumerate().for_each(|(y, c)| {
232 c.iter().enumerate().for_each(|(x, &d)| {
233 let b = frame[y % conf.size.h][x % conf.size.w];
234 frame[y % conf.size.h][x % conf.size.w] = mix(d, b);
235 });
236 });
237 }
238 }
239 frames.push(frame.into_iter().map(|c| c.into_iter().map(|d| (d.0 as u8, d.1 as u8, d.2 as u8, d.3 as u8)).collect::<Vec<_>>()).collect::<Vec<_>>());
240 }
241 frames
242}
243
244fn generate_layers(conf: &Config, ast: Vec<Layer>) -> Vec<LayerPixmap> {
245 let mut layers: Vec<LayerPixmap> = vec![];
246 for l in ast.iter().sorted_by_key(|c| c.index) {
247 if let LayerContent::Still(s) = &l.content {
248 let pixmap = s.iter().map(|c| c.chars().map(|d| to_rgba(conf.colors.get(&d).unwrap_or(&"#000000".to_string()).to_string())).collect::<Vec<_>>()).collect::<Vec<_>>();
249 layers.push(LayerPixmap::Still(pixmap));
250 }
251 if let LayerContent::Video(fs) = &l.content {
252 let mut pixmaps: Frames = vec![];
253 for f in fs.iter().sorted_by_key(|c| c.index) {
254 let pixmap = f.content.iter().map(|c| c.chars().map(|d| to_rgba(conf.colors.get(&d).unwrap_or(&"#000000".to_string()).to_string())).collect::<Vec<_>>()).collect::<Vec<_>>();
255 pixmaps.push(pixmap);
256 }
257 layers.push(LayerPixmap::Video(pixmaps));
258 }
259 }
260 layers
261}
262
263fn to_rgba(hex: String) -> (u8, u8, u8, u8) {
264 let unwrapstr = |r: Option<Match>| -> String {
265 if let Some(m) = r {
266 m.as_str().to_string()
267 } else {
268 "00".to_string()
269 }
270 };
271 let unwrapstr_or = |r: Option<Match>, e: &str| -> String {
272 if let Some(m) = r {
273 m.as_str().to_string()
274 } else {
275 e.to_string()
276 }
277 };
278 let rgba_p = Regex::new(r"^#(?<r>[0-9a-fA-F]{2})(?<g>[0-9a-fA-F]{2})(?<b>[0-9a-fA-F]{2})(?<a>[0-9a-fA-F]{2})?$").unwrap();
279 let caps = rgba_p.captures(&hex).unwrap();
280 let result_m = (caps.name("r"), caps.name("g"), caps.name("b"), caps.name("a"));
281 let result_s = (&unwrapstr(result_m.0), &unwrapstr(result_m.1), &unwrapstr(result_m.2), &unwrapstr_or(result_m.3, "FF"));
282 (
283 u8::from_str_radix(result_s.0, 16).unwrap(),
284 u8::from_str_radix(result_s.1, 16).unwrap(),
285 u8::from_str_radix(result_s.2, 16).unwrap(),
286 u8::from_str_radix(result_s.3, 16).unwrap()
287 )
288}
289
290fn parse(token_r: Vec<Token>) -> Vec<Layer> {
291 let token = &token_r;
292 let mut i = 0usize;
293 let mut layers = Vec::<Layer>::new();
294 while i < token.len() {
295 while let Token::Layer(layern) = token[i] {
296 i += 1;
297 if let Token::Frame(_) = token[i] {
298 let mut frames = Vec::<Frame>::new();
299 while let Token::Frame(framen) = token[i] {
300 i += 1;
301 let mut contents = Vec::<String>::new();
302 while let Token::Normal(s) = &token[i] {
303 contents.push(s.to_owned());
304 i += 1;
305 if i >= token.len() {
306 break;
307 }
308 }
309 frames.push(Frame {
310 index: framen,
311 content: contents
312 });
313 if i >= token.len() {
314 break;
315 }
316 }
317 layers.push(Layer {
318 index: layern,
319 content: LayerContent::Video(frames)
320 });
321 } else {
322 let mut contents = Vec::<String>::new();
323 while let Token::Normal(s) = &token[i] {
324 contents.push(s.to_owned());
325 i += 1;
326 if i >= token.len() {
327 break;
328 }
329 }
330 layers.push(Layer {
331 index: layern,
332 content: LayerContent::Still(contents)
333 });
334 }
335 if i >= token.len() {
336 break;
337 }
338 }
339 }
340 layers
341}
342
343fn tokenize(s: &str) -> Token {
344 if s.contains("## ") {
345 Token::Frame(s.split_at(3).1.parse::<usize>().unwrap())
346 } else if s.contains("# ") {
347 Token::Layer(s.split_at(2).1.parse::<usize>().unwrap())
348 } else {
349 Token::Normal(s.to_owned())
350 }
351}
352
353mod options {
354 use crate::Frames;
355 pub fn order(frames: Frames, order: Vec<usize>) -> Frames {
356 let mut result: Frames = vec![];
357 for &i in &order {
358 result.push((&frames)[i].clone());
359 }
360 result
361 }
362}