Guest User

identicon cleanup

a guest
Jul 7th, 2024
199
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Rust 4.91 KB | None | 0 0
  1. use image::{DynamicImage, Rgb, RgbImage};
  2. use md5::{Digest, Md5};
  3. use std::env;
  4.  
  5. const CHUNK_SIZE: usize = 3;
  6. const GRID_AREA: u8 = 25;
  7.  
  8. #[derive(Default, Debug)]
  9. // some meta data about the identicon image to construct
  10. struct Image {
  11.     hex: Vec<u8>,
  12.     color: [u8; 3], // R,G,B
  13.     // first element in pair is the hex with odd squares filtered out
  14.     // second element is index of that elemen (before filtering)
  15.     grid: Vec<(u8, u8)>,
  16.     // collection of top_right and bottom_left coordinates to be used
  17.     // in painting the canvas
  18.     pixel_map: Vec<((u8, u8), (u8, u8))>,
  19. }
  20.  
  21. fn main() {
  22.     let args: Vec<String> = env::args().collect();
  23.     let user_input = &args[1];
  24.     println!("generating identicon for {user_input}");
  25.     generate_identicon(user_input);
  26. }
  27.  
  28. fn generate_identicon(user_input: &str) {
  29.     // take user input and create identicon.
  30.     // for reference from elixir
  31.     // input
  32.     // |> hash_input()
  33.     // |> pick_color()
  34.     // |> build_grid()
  35.     // |> filter_odd_squares()
  36.     // |> build_pixel_map()
  37.     // |> draw_image()
  38.     // |> save_image(input)
  39.     //
  40.     let input = user_input.as_bytes();
  41.     let mut image: Image = Image {
  42.         hex: hash_input(input),
  43.         ..Default::default()
  44.     };
  45.     pick_color(&mut image);
  46.     build_grid(&mut image);
  47.     filter_odd_squares(&mut image);
  48.     build_pixel_map(&mut image);
  49.     let image_buffer = draw_image(&mut image);
  50.     save_image(image_buffer, user_input);
  51. }
  52.  
  53. fn save_image(image_buffer: RgbImage, user_input: &str) {
  54.     // use user input as file name
  55.     // simplest saving i found?
  56.     let _ = DynamicImage::ImageRgb8(image_buffer).save(format!("{user_input}.png"));
  57. }
  58.  
  59. fn draw_image(image: &mut Image) -> RgbImage {
  60.     // step 1, create 250x250 canvas (image buffer) and then specify fill color
  61.     // then fill each 50x50 square of pixel map with the color using the top_left
  62.     // and bottom right x,y bounds from pixel map, then return the image buffer
  63.     //
  64.     let mut canvas = RgbImage::new(250, 250);
  65.     let color = Rgb(image.color);
  66.     // in our pixel map we have pair of top left and bottom right coords
  67.     // that can define our x and y range in to iterate over to paint on
  68.     for ((tlx, tly), (brx, bry)) in image.pixel_map.clone() {
  69.         for x in tlx..brx {
  70.             for y in tly..bry {
  71.                 // TODO change my struct to use u32 instead of u8 by default
  72.                 // so that i dont need .into()?
  73.                 canvas.put_pixel(x.into(), y.into(), color);
  74.             }
  75.         }
  76.     }
  77.     canvas
  78. }
  79.  
  80. fn build_pixel_map(image: &mut Image) {
  81.     // for ref
  82.     // horizontal = (idx % 5) * 50;
  83.     // vertical = (idx / 5) * 50;
  84.     // top_left = (horizontal, vertical);
  85.     // bottom_right = (horizontal + 50, vertical + 50);
  86.     // return -> (top_left, bottom_right)
  87.     image.pixel_map = image
  88.         .grid
  89.         .clone()
  90.         .into_iter()
  91.         .map(|(_, idx)| {
  92.             (
  93.                 ((idx % 5) * 50, idx / 5 * 50),
  94.                 ((idx % 5) * 50 + 50, idx / 5 * 50 + 50),
  95.             )
  96.         })
  97.         .collect();
  98. }
  99.  
  100. fn filter_odd_squares(image: &mut Image) {
  101.     // TODO: better way to do this?
  102.     image.grid = image
  103.         .grid
  104.         .clone()
  105.         .into_iter()
  106.         .filter(|(x, _)| x % 2 == 0)
  107.         .collect();
  108. }
  109.  
  110. fn mirror_row(temp_grid: &mut Vec<Vec<u8>>) {
  111.     // for every "row" of the grid, want to add the first two
  112.     // elements to the last, in reverse order
  113.     // e.g.
  114.     // [1,2,3] -> [1,2,3,2,1]
  115.     let mut temp_clone = temp_grid.clone();
  116.     for (pos, row) in temp_grid.iter().enumerate() {
  117.         temp_clone[pos].push(row[1]);
  118.         temp_clone[pos].push(row[0]);
  119.     }
  120.     // deref kill the clone? TODO: is this necessary/efficient?
  121.     *temp_grid = temp_clone;
  122. }
  123.  
  124. fn build_grid(image: &mut Image) {
  125.     let mut tmp: Vec<Vec<u8>> = vec![vec![]];
  126.     tmp.pop(); //remove first [], TODO: better way to init the vec of vecs?
  127.     for el in image.hex.chunks(CHUNK_SIZE) {
  128.         if el.len() == CHUNK_SIZE {
  129.             tmp.push(el.to_vec());
  130.         }
  131.     }
  132.     // step 2, mirror row
  133.     mirror_row(&mut tmp);
  134.     // step 3, flatten grid
  135.     let tmp_flat = tmp.into_iter().flatten().collect::<Vec<u8>>();
  136.     // step 4, get index to pair with flattened grid
  137.     let tmp_idx = (0..GRID_AREA).collect::<Vec<u8>>();
  138.     // pair grid with idx
  139.     image.grid = tmp_flat.clone().into_iter().zip(tmp_idx.clone()).collect();
  140. }
  141.  
  142. fn hash_input(input: &[u8]) -> Vec<u8> {
  143.     // this md-5 seems more verbose than the other regular md5 lib?
  144.     // TODO: look into what the differences are later
  145.     // let digest = md5::compute(b"qwerty");
  146.     // vs
  147.     // let digest = md5::Md5::new().update(b"qwerty")
  148.     let mut digest2 = Md5::new();
  149.     digest2.update(input);
  150.     digest2.finalize().to_vec()
  151. }
  152.  
  153. fn pick_color(image: &mut Image) {
  154.     image.color.copy_from_slice(&image.hex[..3]);
  155. }
  156.  
Advertisement
Add Comment
Please, Sign In to add comment