1use self::super::util::{ANSI_BG_COLOUR_ESCAPES, ANSI_RESET_ATTRIBUTES, ANSI_COLOUR_ESCAPES, JPEG_MAGIC, BMP_MAGIC, ICO_MAGIC, GIF_MAGIC, PNG_MAGIC,
11 closest_colour, bg_colours_for};
12use image::{self, GenericImageView, DynamicImage, ImageFormat, Pixel};
13use std::io::{BufReader, Write, Read};
14use image::imageops::FilterType;
15use self::super::Error;
16use std::path::PathBuf;
17use std::ops::Index;
18use std::fs::File;
19
20mod no_ansi;
21
22pub use self::no_ansi::write_no_ansi;
23
24
25pub fn guess_format(file: &(String, PathBuf)) -> Result<ImageFormat, Error> {
56 file.1
57 .extension()
58 .and_then(|ext| match &ext.to_str().unwrap().to_lowercase()[..] {
59 "png" => Some(Ok(ImageFormat::Png)),
60 "jpg" | "jpeg" | "jpe" | "jif" | "jfif" | "jfi" => Some(Ok(ImageFormat::Jpeg)),
61 "gif" => Some(Ok(ImageFormat::Gif)),
62 "webp" => Some(Ok(ImageFormat::WebP)),
63 "ppm" => Some(Ok(ImageFormat::Pnm)),
64 "tiff" | "tif" => Some(Ok(ImageFormat::Tiff)),
65 "tga" => Some(Ok(ImageFormat::Tga)),
66 "bmp" | "dib" => Some(Ok(ImageFormat::Bmp)),
67 "ico" => Some(Ok(ImageFormat::Ico)),
68 "hdr" => Some(Ok(ImageFormat::Hdr)),
69 _ => None,
70 })
71 .unwrap_or_else(|| {
72 let mut buf = [0; 32];
73 let read = File::open(&file.1).map_err(|_| Error::OpeningImageFailed(file.0.clone()))?.read(&mut buf).unwrap();
74 let buf = &buf[..read];
75
76 if buf.len() >= PNG_MAGIC.len() && &buf[..PNG_MAGIC.len()] == PNG_MAGIC {
77 Ok(ImageFormat::Png)
78 } else if buf.len() >= JPEG_MAGIC.len() && &buf[..JPEG_MAGIC.len()] == JPEG_MAGIC {
79 Ok(ImageFormat::Jpeg)
80 } else if buf.len() >= GIF_MAGIC.len() && &buf[..GIF_MAGIC.len()] == GIF_MAGIC {
81 Ok(ImageFormat::Gif)
82 } else if buf.len() >= BMP_MAGIC.len() && &buf[..BMP_MAGIC.len()] == BMP_MAGIC {
83 Ok(ImageFormat::Bmp)
84 } else if buf.len() >= ICO_MAGIC.len() && &buf[..ICO_MAGIC.len()] == ICO_MAGIC {
85 Ok(ImageFormat::Ico)
86 } else {
87 Err(Error::GuessingFormatFailed(file.0.clone()))
88 }
89 })
90}
91
92pub fn load_image(file: &(String, PathBuf), format: ImageFormat) -> Result<DynamicImage, Error> {
96 Ok(image::load(BufReader::new(File::open(&file.1).map_err(|_| Error::OpeningImageFailed(file.0.clone()))?),
97 format)
98 .unwrap())
99}
100
101pub fn image_resized_size(size: (u32, u32), term_size: (u32, u32), preserve_aspect: bool) -> (u32, u32) {
105 if !preserve_aspect {
106 return (term_size.0, term_size.1 * 2);
107 }
108
109 let nwidth = term_size.0;
110 let nheight = term_size.1 * 2;
111 let (width, height) = size;
112
113 let ratio = width as f32 / height as f32;
114 let nratio = nwidth as f32 / nheight as f32;
115
116 let scale = if nratio > ratio {
117 nheight as f32 / height as f32
118 } else {
119 nwidth as f32 / width as f32
120 };
121
122 ((width as f32 * scale) as u32, (height as f32 * scale) as u32)
123}
124
125pub fn resize_image(img: &DynamicImage, size: (u32, u32)) -> DynamicImage {
127 img.resize_exact(size.0, size.1, FilterType::Nearest)
128}
129
130pub fn create_colourtable<C: Index<usize, Output = u8>>(img: &DynamicImage, upper_colours: &[C], lower_colours: &[C]) -> Vec<Vec<(usize, usize)>> {
154 let (width, height) = img.dimensions();
155 let term_h = height / 2;
156
157 (0..term_h)
158 .map(|y| {
159 let upper_y = y * 2;
160 let lower_y = upper_y + 1;
161
162 (0..width)
163 .map(|x| (closest_colour(img.get_pixel(x, upper_y).to_rgb(), upper_colours), closest_colour(img.get_pixel(x, lower_y).to_rgb(), lower_colours)))
164 .collect()
165 })
166 .collect()
167}
168
169pub fn write_ansi<W: Write, C: Index<usize, Output = u8>>(out: &mut W, img: &DynamicImage, foreground_colours: &[C]) {
171 for line in create_colourtable(img, foreground_colours, &bg_colours_for(foreground_colours)) {
172 for (upper_clr, lower_clr) in line {
173 write!(out,
174 "{}{}\u{2580}", ANSI_COLOUR_ESCAPES[upper_clr],
176 ANSI_BG_COLOUR_ESCAPES[lower_clr])
177 .unwrap();
178 }
179 writeln!(out, "{}", ANSI_RESET_ATTRIBUTES).unwrap();
180 }
181}
182
183pub fn write_ansi_truecolor<W: Write>(out: &mut W, img: &DynamicImage) {
185 let (width, height) = img.dimensions();
186 let term_h = height / 2;
187
188 for y in 0..term_h {
189 let upper_y = y * 2;
190 let lower_y = upper_y + 1;
191
192 for x in 0..width {
193 let upper_pixel = img.get_pixel(x, upper_y).to_rgb();
194 let lower_pixel = img.get_pixel(x, lower_y).to_rgb();
195
196 write!(out,
197 "\x1B[38;2;{};{};{}m\
198 \x1B[48;2;{};{};{}m\u{2580}", upper_pixel[0],
200 upper_pixel[1],
201 upper_pixel[2],
202 lower_pixel[0],
203 lower_pixel[1],
204 lower_pixel[2])
205 .unwrap();
206 }
207 writeln!(out, "{}", ANSI_RESET_ATTRIBUTES).unwrap();
208 }
209}