1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
extern crate image; extern crate gif; use std::process::Command; use std::error::Error; use std::path::Path; use std::fs; use std::fs::File; use std::io::prelude::*; use image::{GenericImage, ImageFormat, guess_format}; use gif::Decoder; #[derive(Debug, PartialEq)] pub struct Info { pub format: ImageFormat, pub width: u32, pub height: u32, pub frames: u32, } pub fn info(path: &Path) -> Result<Info, Box<Error>> { let mut im = File::open(path)?; let mut buf = [0; 16]; im.read(&mut buf)?; let format = guess_format(&buf)?; let im = image::open(path)?; let (width, height) = im.dimensions(); let frames = match format { ImageFormat::GIF => { let decoder = Decoder::new(File::open(path)?); let mut reader = decoder.read_info().unwrap(); let mut frames = 0; while let Some(_) = reader.next_frame_info().unwrap() { frames += 1; } frames } _ => 1, }; Ok(Info { format: format, width: width, height: height, frames: frames, }) } pub fn crop(src: &Path, x: u32, y: u32, width: u32, height: u32, dest: &Path) -> Result<bool, Box<Error>> { let inf = info(src)?; if x + width > inf.width || y + height > inf.height { panic!("out of existing image bounds"); } let srcs = src.to_str().unwrap(); let dests = dest.to_str().unwrap(); let dims = format!("{}x{}+{}+{}", width, height, x, y); let cmd = match inf.format { ImageFormat::GIF => { Command::new("convert").arg(srcs) .arg("-coalesce") .arg("-repage") .arg("0x0") .arg("-crop") .arg(dims) .arg("+repage") .arg(dests) .output()? } _ => { Command::new("convert").arg(srcs) .arg("-crop") .arg(dims) .arg(dests) .output()? } }; Ok(cmd.status.success()) } pub fn resize(src: &Path, width: u32, height: u32, dest: &Path) -> Result<bool, Box<Error>> { let inf = info(src)?; let mut srcs = src.to_str().unwrap(); let dests = dest.to_str().unwrap(); let temp = match inf.format { ImageFormat::GIF => { let cmd = Command::new("convert").arg(srcs) .arg("-coalesce") .arg(dests) .output()?; srcs = dests; cmd.status.success() } _ => false, }; let success = Command::new("convert") .arg("-size") .arg(format!("{}x{}", inf.width, inf.height)) .arg(srcs) .arg("-resize") .arg(format!("{}x{}", width, height)) .arg(dests) .output()? .status .success(); if temp && !success { fs::remove_file(dests)?; } Ok(success) }