use image::{ImageBuffer, Luma}; use proc_macro::{Span, TokenStream}; use quote::quote; use regex::Regex; use syn::parse::Parse; fn remove_non_alphanumeric(input: &str) -> String { let re = Regex::new(r"[^a-zA-Z0-9_]+").unwrap(); re.replace_all(input, "").to_string() } fn to_format(img: ImageBuffer, Vec>, width: usize, height: usize) -> Vec { let mut output = Vec::new(); let mut bit: u8 = 0; let mut byte: u8 = 0; for y in 0..height { for x in 0..width { let pixel = img.get_pixel(x as u32, y as u32)[0]; if pixel >= 127 { byte |= 1 << bit; } bit += 1; if bit == 8 { output.push(byte); byte = 0; bit = 0; } } } if bit != 0 { output.push(byte); } output } fn path_to_image(path: &str) -> (Vec, String, usize, usize) { let img = match image::open(path) { Ok(img) => img.to_luma8(), Err(e) => panic!("failed to open image {}: {}", path, e), }; let width = img.width() as usize; let height = img.height() as usize; let bytes = to_format(img, width, height); let path = path .split('/') .next_back() .expect("failed to get last part of path"); let split: Vec<_> = path.split('.').collect(); let name = remove_non_alphanumeric(&split[0..split.len() - 1].join(".")).to_uppercase(); (bytes, name, width, height) } struct ParsedArgs { path: String, } impl Parse for ParsedArgs { fn parse(input: syn::parse::ParseStream) -> syn::Result { let path: syn::LitStr = input.parse()?; let path = path.value(); Ok(ParsedArgs { path }) } } pub fn parse_image( input: TokenStream, ) -> Result< ( u8, u8, proc_macro2::Ident, usize, Vec, ), syn::Error, > { // parse the input into a comma separated list of arguments let parsed_args = syn::parse::(input)?; // let parsed_args = parse_macro_input!(input as ParsedArgs); let (bytes, name, width, height) = path_to_image(&parsed_args.path); let width = width as u8; let height = height as u8; let byte_array = bytes.as_slice(); let byte_count = byte_array.len(); let name_ident = syn::Ident::new(&name, Span::call_site().into()); let byte_tokens = bytes.iter().map(|b| quote! { #b }).collect::>(); Ok((width, height, name_ident, byte_count, byte_tokens)) } pub fn include_font_plate_impl(input: TokenStream) -> TokenStream { let (_, _, _, _, byte_tokens) = parse_image(input).unwrap(); let output = quote! { [#(#byte_tokens),*] }; output.into() }