Skip to main content

pixdown/
lib.rs

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 { // (R, G, B, A)
49    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    { // IHDR
110        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")); // tEXT
118    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]); // [Null separator, Compression flag: true, Compression method: deflate-zlib, Null separator, Null separator]
124            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 { // acTL
129        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 { // fcTL
138            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![]; // fdAT
154            {
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}