æ§ã ãªç»åãææ» ããã®ã§æ°ãä»ãããï¼
ããã«åããã¾ã
ããããããªããã¼ã«ãä½æãã¾ãã
% git clone https://github.com/windymelt/superify.git % cd superify % cargo build --release % ./target/release/superify windymelt.png % open windymelt.png.animated.gif
ãã§ããã§ããã
ã²ã¼ãã³ã°GIFãä½ããã
èªç²ç²ããªãã¦è¨èãããã¾ããåãç²ãã¦ãã¾ããããã¦æãã話é¡ã欲ãããã®ããããªä¸åã¯Party Parrotã好ãã§ãä½ã®èª¬æãç¡ãã«ã²ã¼ãã³ã°PCããã¦çºå ãããªã¦ã ãè¦ãã¨ãªããé¢ç½ãæ°åã«ãªã£ã¦ãã¾ãã¾ãã
åãç»åãè¹è²ã«å ãããããä»®ã«ã²ã¼ãã³ã°GIFã¨å¼ã¶ãã¨ã«ãã¾ããããSlackã®çµµæåã«è¨å®ããã¨çç¬å¿ è³ã社å ã®äººæ°è ã§ãã
GIMPã§ããããä½æ¥ãããã¨ã§ä½ããã¨ãã§ããã®ã§ããããããã¨ã¯åããªã®ã§èªååãããã欲ããã¨ãã«ä¸ç¬ã§ããã£ãGIFãæã«å ¥ãããã®ã§ãããã°ã©ã ãä½ããã¨ã«ãã¾ããã
ã©ãããã°ããã
ã©ãããã°ã²ã¼ãã³ã°GIFãä½ããã®ãèãã¦ã¿ã¾ãã
- ç»åãèªã¿åã
- å ç»åã®è²ç¸ç°ï¼HSVã®Hueï¼ãæ°Â°å転ãããç»åãçæãã
- 1å¨ããã¾ã§ç¹°ãè¿ã
- å転ããç»åã1ã¤ã®ã¢ãã¡ã¼ã·ã§ã³GIFã«ãã
ããããæµãã§ããããã§ãã
å®è£
ç»åã®èªã¿è¾¼ã¿ãã§ãã¦GIFãåºåã§ããè¨èªãã¨ãããã©ã¤ãã©ãªãããã°ä½ã§ãããããã§ãããã²ã¨ã¾ãæè¿åå¼·ãã¦ããRustã§ãã£ã¦ã¿ããã¨æãã¾ããããªããéããã ãã
è²ç¸å転ã¯image
ã©ã¤ãã©ãªã§å¯è½ã§ããè²ç¸ã15°å転ãããç»åã24åçæãä¸åº¦ãã£ã¹ã¯ã«ä¿åãã¾ãã次ã«ãEngiffen
ã¨ããç»åãã¢ãã¡ã¼ã·ã§ã³GIFã«å¤æããã©ã¤ãã©ãªã使ããçµåãã¦ãããã¾ããfpsã¯24ã«è¨å®ãã1ç§ã§1å転ããããã«ãªãã¾ãã
ä¸åº¦ä¿åããã®ã¯ãEngiffen
ã®ã¤ã³ã¿ã¼ãã§ã¤ã¹ã®é½åã§ãã
ã§ããã³ã¼ãã§ãã
use engiffen::*; use image::imageops::*; use std::fs::File; fn main() { let args: Vec<String> = std::env::args().collect(); if args.len() < 2 { exit_with_message("Please provide image file path"); } let filename = &args[1]; let outfilename = format!("{}.animated.gif", filename); let img = match image::open(filename) { Ok(img) => img, Err(_) => return exit_with_message("Failed to open file"), }; for i in 0..24 { let img2 = colorops::huerotate(&img, 15 * i); // 24frameã§360ã«ãªã match img2.save(format!("out_{}.png", i)) { Ok(_) => print!("."), Err(_) => exit_with_message("Failed to save image"), } } println!(); let paths = vec![ "out_0.png", "out_1.png", "out_2.png", "out_3.png", "out_4.png", "out_5.png", "out_6.png", "out_7.png", "out_8.png", "out_9.png", "out_10.png", "out_11.png", "out_12.png", "out_13.png", "out_14.png", "out_15.png", "out_16.png", "out_17.png", "out_18.png", "out_19.png", "out_20.png", "out_21.png", "out_22.png", "out_23.png", ]; let images = engiffen::load_images(&paths); let gif = match engiffen(&images, 24, Quantizer::NeuQuant(2)) { Ok(g) => g, Err(_) => return exit_with_message("making gif failed"), }; let mut outfile = match File::create(&outfilename) { Ok(f) => f, Err(_) => return exit_with_message(&format!("failed to create {}", &outfilename)), }; match gif.write(&mut outfile) { Ok(_) => (), Err(_) => return exit_with_message(&format!("failed to write {}", &outfilename)), }; for p in paths { match std::fs::remove_file(p) { Ok(_) => (), Err(_) => return exit_with_message(&format!("failed to remove temporary file {}", p)), }; print!("."); } println!("done!"); } fn exit_with_message(message: &str) { eprintln!("*** {}", message); std::process::exit(1); }
ãã¡ã¤ã«åããã¿æ¸ããã¦ããã¨ãããããµãã®ã§ãããã¾ãåãã®ã§è¯ããã¨æãã¾ãããåä½ã¯ããªãéãããã¾ã®ã¨ããã¹ãã¬ã¹ã¯æãã¾ããã
工夫ããã¨ãã
Rustã¯æåããã失æãããããããªãé¢æ°ã®å®è¡çµæãResult
ã¨ããåã§è¿ãã¦ãã¾ãããããã¿ã¤ãã§ã¯é©å½ã«.unwrap()
ãå¼ãã§åããã¦ãã¾ããï¼scalaã§è¨ãã¨Option.get
ã¿ãããªå±éºãªã¡ã½ããã§ãï¼ããæçµçã«ã¡ããã¨match
ã§åå²ãã¦å¦çãããããã«ãã¾ããããã®ãã¿ã¼ã³ã§æ¸ãã®ããã¹ããªã®ãã¯ãããããã¾ãããif
ã§åå²ããããã¯ã¡ãã£ã¨ç¶ºéºããªï¼ã¨ããæãã§ããã¢ããããã£ããããã§ããã
ããããã°Rustã®borrowã¾ããã¯ãããåãããããã£ãã¨æãã¾ãã
C++ã®unique_ptr
ãã辿ã£ã¦èª¬æããã¦ããã®ã§ãããªãç解ã§ãã¾ããã
ããã£ãã§ãã
cargo build --release
ããçµæã®ãã¤ããªãµã¤ãºã¯5.6MiBã§ãããstrip
ããã2.2MiBã«ã¾ã§æ¸ãã¾ããããæ´»ç¨ãã ããã