├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md ├── docs └── screenshots │ ├── ferris_termage.png │ ├── pika_termage.gif │ ├── pika_termage.png │ └── termage.png └── src ├── bin └── main.rs └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | **/*.rs.bk 3 | imgs 4 | ferris.png 5 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "adler32" 5 | version = "1.2.0" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" 8 | 9 | [[package]] 10 | name = "ansi_term" 11 | version = "0.11.0" 12 | source = "registry+https://github.com/rust-lang/crates.io-index" 13 | checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" 14 | dependencies = [ 15 | "winapi", 16 | ] 17 | 18 | [[package]] 19 | name = "atty" 20 | version = "0.2.14" 21 | source = "registry+https://github.com/rust-lang/crates.io-index" 22 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 23 | dependencies = [ 24 | "hermit-abi", 25 | "libc", 26 | "winapi", 27 | ] 28 | 29 | [[package]] 30 | name = "autocfg" 31 | version = "1.0.1" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 34 | 35 | [[package]] 36 | name = "bitflags" 37 | version = "1.2.1" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 40 | 41 | [[package]] 42 | name = "byteorder" 43 | version = "1.4.3" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 46 | 47 | [[package]] 48 | name = "cfg-if" 49 | version = "1.0.0" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 52 | 53 | [[package]] 54 | name = "clap" 55 | version = "2.33.3" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" 58 | dependencies = [ 59 | "ansi_term", 60 | "atty", 61 | "bitflags", 62 | "strsim", 63 | "textwrap", 64 | "unicode-width", 65 | "vec_map", 66 | ] 67 | 68 | [[package]] 69 | name = "color_quant" 70 | version = "1.1.0" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" 73 | 74 | [[package]] 75 | name = "crossbeam-channel" 76 | version = "0.5.0" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775" 79 | dependencies = [ 80 | "cfg-if", 81 | "crossbeam-utils", 82 | ] 83 | 84 | [[package]] 85 | name = "crossbeam-deque" 86 | version = "0.8.0" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9" 89 | dependencies = [ 90 | "cfg-if", 91 | "crossbeam-epoch", 92 | "crossbeam-utils", 93 | ] 94 | 95 | [[package]] 96 | name = "crossbeam-epoch" 97 | version = "0.9.3" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "2584f639eb95fea8c798496315b297cf81b9b58b6d30ab066a75455333cf4b12" 100 | dependencies = [ 101 | "cfg-if", 102 | "crossbeam-utils", 103 | "lazy_static", 104 | "memoffset", 105 | "scopeguard", 106 | ] 107 | 108 | [[package]] 109 | name = "crossbeam-utils" 110 | version = "0.8.3" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49" 113 | dependencies = [ 114 | "autocfg", 115 | "cfg-if", 116 | "lazy_static", 117 | ] 118 | 119 | [[package]] 120 | name = "deflate" 121 | version = "0.7.20" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | checksum = "707b6a7b384888a70c8d2e8650b3e60170dfc6a67bb4aa67b6dfca57af4bedb4" 124 | dependencies = [ 125 | "adler32", 126 | "byteorder", 127 | ] 128 | 129 | [[package]] 130 | name = "either" 131 | version = "1.6.1" 132 | source = "registry+https://github.com/rust-lang/crates.io-index" 133 | checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" 134 | 135 | [[package]] 136 | name = "gif" 137 | version = "0.10.3" 138 | source = "registry+https://github.com/rust-lang/crates.io-index" 139 | checksum = "471d90201b3b223f3451cd4ad53e34295f16a1df17b1edf3736d47761c3981af" 140 | dependencies = [ 141 | "color_quant", 142 | "lzw", 143 | ] 144 | 145 | [[package]] 146 | name = "hermit-abi" 147 | version = "0.1.18" 148 | source = "registry+https://github.com/rust-lang/crates.io-index" 149 | checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" 150 | dependencies = [ 151 | "libc", 152 | ] 153 | 154 | [[package]] 155 | name = "image" 156 | version = "0.19.0" 157 | source = "registry+https://github.com/rust-lang/crates.io-index" 158 | checksum = "ebdff791af04e30089bde8ad2a632b86af433b40c04db8d70ad4b21487db7a6a" 159 | dependencies = [ 160 | "byteorder", 161 | "gif", 162 | "jpeg-decoder", 163 | "lzw", 164 | "num-derive", 165 | "num-iter", 166 | "num-rational", 167 | "num-traits", 168 | "png", 169 | "scoped_threadpool", 170 | ] 171 | 172 | [[package]] 173 | name = "inflate" 174 | version = "0.4.5" 175 | source = "registry+https://github.com/rust-lang/crates.io-index" 176 | checksum = "1cdb29978cc5797bd8dcc8e5bf7de604891df2a8dc576973d71a281e916db2ff" 177 | dependencies = [ 178 | "adler32", 179 | ] 180 | 181 | [[package]] 182 | name = "jpeg-decoder" 183 | version = "0.1.22" 184 | source = "registry+https://github.com/rust-lang/crates.io-index" 185 | checksum = "229d53d58899083193af11e15917b5640cd40b29ff475a1fe4ef725deb02d0f2" 186 | dependencies = [ 187 | "rayon", 188 | ] 189 | 190 | [[package]] 191 | name = "lazy_static" 192 | version = "1.4.0" 193 | source = "registry+https://github.com/rust-lang/crates.io-index" 194 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 195 | 196 | [[package]] 197 | name = "libc" 198 | version = "0.2.92" 199 | source = "registry+https://github.com/rust-lang/crates.io-index" 200 | checksum = "56d855069fafbb9b344c0f962150cd2c1187975cb1c22c1522c240d8c4986714" 201 | 202 | [[package]] 203 | name = "lzw" 204 | version = "0.10.0" 205 | source = "registry+https://github.com/rust-lang/crates.io-index" 206 | checksum = "7d947cbb889ed21c2a84be6ffbaebf5b4e0f4340638cba0444907e38b56be084" 207 | 208 | [[package]] 209 | name = "memoffset" 210 | version = "0.6.3" 211 | source = "registry+https://github.com/rust-lang/crates.io-index" 212 | checksum = "f83fb6581e8ed1f85fd45c116db8405483899489e38406156c25eb743554361d" 213 | dependencies = [ 214 | "autocfg", 215 | ] 216 | 217 | [[package]] 218 | name = "num-derive" 219 | version = "0.2.5" 220 | source = "registry+https://github.com/rust-lang/crates.io-index" 221 | checksum = "eafd0b45c5537c3ba526f79d3e75120036502bebacbb3f3220914067ce39dbf2" 222 | dependencies = [ 223 | "proc-macro2", 224 | "quote", 225 | "syn", 226 | ] 227 | 228 | [[package]] 229 | name = "num-integer" 230 | version = "0.1.44" 231 | source = "registry+https://github.com/rust-lang/crates.io-index" 232 | checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" 233 | dependencies = [ 234 | "autocfg", 235 | "num-traits", 236 | ] 237 | 238 | [[package]] 239 | name = "num-iter" 240 | version = "0.1.42" 241 | source = "registry+https://github.com/rust-lang/crates.io-index" 242 | checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59" 243 | dependencies = [ 244 | "autocfg", 245 | "num-integer", 246 | "num-traits", 247 | ] 248 | 249 | [[package]] 250 | name = "num-rational" 251 | version = "0.1.42" 252 | source = "registry+https://github.com/rust-lang/crates.io-index" 253 | checksum = "ee314c74bd753fc86b4780aa9475da469155f3848473a261d2d18e35245a784e" 254 | dependencies = [ 255 | "num-integer", 256 | "num-traits", 257 | ] 258 | 259 | [[package]] 260 | name = "num-traits" 261 | version = "0.2.14" 262 | source = "registry+https://github.com/rust-lang/crates.io-index" 263 | checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" 264 | dependencies = [ 265 | "autocfg", 266 | ] 267 | 268 | [[package]] 269 | name = "num_cpus" 270 | version = "1.13.0" 271 | source = "registry+https://github.com/rust-lang/crates.io-index" 272 | checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" 273 | dependencies = [ 274 | "hermit-abi", 275 | "libc", 276 | ] 277 | 278 | [[package]] 279 | name = "png" 280 | version = "0.12.0" 281 | source = "registry+https://github.com/rust-lang/crates.io-index" 282 | checksum = "f54b9600d584d3b8a739e1662a595fab051329eff43f20e7d8cc22872962145b" 283 | dependencies = [ 284 | "bitflags", 285 | "deflate", 286 | "inflate", 287 | "num-iter", 288 | ] 289 | 290 | [[package]] 291 | name = "proc-macro2" 292 | version = "0.4.30" 293 | source = "registry+https://github.com/rust-lang/crates.io-index" 294 | checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" 295 | dependencies = [ 296 | "unicode-xid", 297 | ] 298 | 299 | [[package]] 300 | name = "quote" 301 | version = "0.6.13" 302 | source = "registry+https://github.com/rust-lang/crates.io-index" 303 | checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" 304 | dependencies = [ 305 | "proc-macro2", 306 | ] 307 | 308 | [[package]] 309 | name = "rayon" 310 | version = "1.5.0" 311 | source = "registry+https://github.com/rust-lang/crates.io-index" 312 | checksum = "8b0d8e0819fadc20c74ea8373106ead0600e3a67ef1fe8da56e39b9ae7275674" 313 | dependencies = [ 314 | "autocfg", 315 | "crossbeam-deque", 316 | "either", 317 | "rayon-core", 318 | ] 319 | 320 | [[package]] 321 | name = "rayon-core" 322 | version = "1.9.0" 323 | source = "registry+https://github.com/rust-lang/crates.io-index" 324 | checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a" 325 | dependencies = [ 326 | "crossbeam-channel", 327 | "crossbeam-deque", 328 | "crossbeam-utils", 329 | "lazy_static", 330 | "num_cpus", 331 | ] 332 | 333 | [[package]] 334 | name = "scoped_threadpool" 335 | version = "0.1.9" 336 | source = "registry+https://github.com/rust-lang/crates.io-index" 337 | checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" 338 | 339 | [[package]] 340 | name = "scopeguard" 341 | version = "1.1.0" 342 | source = "registry+https://github.com/rust-lang/crates.io-index" 343 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 344 | 345 | [[package]] 346 | name = "strsim" 347 | version = "0.8.0" 348 | source = "registry+https://github.com/rust-lang/crates.io-index" 349 | checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" 350 | 351 | [[package]] 352 | name = "syn" 353 | version = "0.15.44" 354 | source = "registry+https://github.com/rust-lang/crates.io-index" 355 | checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" 356 | dependencies = [ 357 | "proc-macro2", 358 | "quote", 359 | "unicode-xid", 360 | ] 361 | 362 | [[package]] 363 | name = "termage" 364 | version = "1.1.1" 365 | dependencies = [ 366 | "clap", 367 | "image", 368 | "terminal_graphics", 369 | "terminal_size", 370 | ] 371 | 372 | [[package]] 373 | name = "terminal_graphics" 374 | version = "0.1.5" 375 | source = "registry+https://github.com/rust-lang/crates.io-index" 376 | checksum = "f3585912e123cb72d56d923dde7419b329481d4ed76cb01d8c0d733281484edb" 377 | 378 | [[package]] 379 | name = "terminal_size" 380 | version = "0.1.16" 381 | source = "registry+https://github.com/rust-lang/crates.io-index" 382 | checksum = "86ca8ced750734db02076f44132d802af0b33b09942331f4459dde8636fd2406" 383 | dependencies = [ 384 | "libc", 385 | "winapi", 386 | ] 387 | 388 | [[package]] 389 | name = "textwrap" 390 | version = "0.11.0" 391 | source = "registry+https://github.com/rust-lang/crates.io-index" 392 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 393 | dependencies = [ 394 | "unicode-width", 395 | ] 396 | 397 | [[package]] 398 | name = "unicode-width" 399 | version = "0.1.8" 400 | source = "registry+https://github.com/rust-lang/crates.io-index" 401 | checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" 402 | 403 | [[package]] 404 | name = "unicode-xid" 405 | version = "0.1.0" 406 | source = "registry+https://github.com/rust-lang/crates.io-index" 407 | checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" 408 | 409 | [[package]] 410 | name = "vec_map" 411 | version = "0.8.2" 412 | source = "registry+https://github.com/rust-lang/crates.io-index" 413 | checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" 414 | 415 | [[package]] 416 | name = "winapi" 417 | version = "0.3.9" 418 | source = "registry+https://github.com/rust-lang/crates.io-index" 419 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 420 | dependencies = [ 421 | "winapi-i686-pc-windows-gnu", 422 | "winapi-x86_64-pc-windows-gnu", 423 | ] 424 | 425 | [[package]] 426 | name = "winapi-i686-pc-windows-gnu" 427 | version = "0.4.0" 428 | source = "registry+https://github.com/rust-lang/crates.io-index" 429 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 430 | 431 | [[package]] 432 | name = "winapi-x86_64-pc-windows-gnu" 433 | version = "0.4.0" 434 | source = "registry+https://github.com/rust-lang/crates.io-index" 435 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 436 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "termage" 3 | version = "1.1.1" 4 | authors = ["Calum Forster "] 5 | readme = "README.md" 6 | keywords = ["terminal", "image", "view", "termage"] 7 | repository = "https://github.com/calum/terminal_image_display" 8 | license = "MIT/Apache-2.0" 9 | description = "Display images in the terminal!" 10 | exclude = ["docs/*"] 11 | 12 | [[bin]] 13 | name = "termage" 14 | path = "src/bin/main.rs" 15 | doc = false 16 | 17 | [lib] 18 | name = "termage" 19 | path = "src/lib.rs" 20 | 21 | [dependencies] 22 | image = "0.19.0" 23 | terminal_graphics = "0.1.5" 24 | terminal_size = "0.1.7" 25 | clap = "2.29.4" 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | Termage 4 | 5 |
6 | 7 |

View images and gifs in your terminal! 🖼️🖥️

8 | 9 | ## Install 10 | 11 | You can install using [Cargo](https://crates.io/) 12 | ``` 13 | cargo install termage 14 | ``` 15 | 16 | or you can clone the repo and build the binary 17 | ``` 18 | git clone https://github.com/calum/terminal_image_display 19 | cd terminal_image_display 20 | cargo run -- --image ferris.png 21 | ``` 22 | 23 | ## Usage 24 | ``` 25 | $ termage --help 26 | 27 | Termage 1.0.1 28 | https://github.com/calum/terminal_image_display 29 | Display any image in the terminal with Termage! 30 | 31 | USAGE: 32 | termage [OPTIONS] 33 | 34 | FLAGS: 35 | -h, --help Prints help information 36 | -V, --version Prints version information 37 | 38 | OPTIONS: 39 | -g, --gif Input animated gif filepath 40 | -i, --image Input image filepath 41 | ``` 42 | 43 | ## Example output 44 | ``` 45 | termage -i ferris.png 46 | ``` 47 | ![](https://raw.githubusercontent.com/calum/terminal_image_display/master/docs/screenshots/ferris_termage.png) 48 | 49 | ``` 50 | termage -g pika.gif 51 | ``` 52 | ![](https://raw.githubusercontent.com/calum/terminal_image_display/master/docs/screenshots/pika_termage.gif) 53 | 54 | ``` 55 | termage -i pika.png 56 | ``` 57 | ![](https://raw.githubusercontent.com/calum/terminal_image_display/master/docs/screenshots/pika_termage.png) 58 | -------------------------------------------------------------------------------- /docs/screenshots/ferris_termage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calum/terminal_image_display/b18efbf6e6eeedf4bc519fac1d333c333047c423/docs/screenshots/ferris_termage.png -------------------------------------------------------------------------------- /docs/screenshots/pika_termage.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calum/terminal_image_display/b18efbf6e6eeedf4bc519fac1d333c333047c423/docs/screenshots/pika_termage.gif -------------------------------------------------------------------------------- /docs/screenshots/pika_termage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calum/terminal_image_display/b18efbf6e6eeedf4bc519fac1d333c333047c423/docs/screenshots/pika_termage.png -------------------------------------------------------------------------------- /docs/screenshots/termage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calum/terminal_image_display/b18efbf6e6eeedf4bc519fac1d333c333047c423/docs/screenshots/termage.png -------------------------------------------------------------------------------- /src/bin/main.rs: -------------------------------------------------------------------------------- 1 | extern crate image; 2 | extern crate termage; 3 | extern crate terminal_graphics; 4 | extern crate terminal_size; 5 | extern crate clap; 6 | 7 | use terminal_size::{Width, Height, terminal_size}; 8 | 9 | use termage::{display_image, display_gif}; 10 | 11 | use clap::{Arg, App}; 12 | 13 | fn main() { 14 | let matches = App::new("Termage") 15 | .version("1.0.1") 16 | .about("Display any image in the terminal with Termage!") 17 | .author("https://github.com/calum/terminal_image_display") 18 | .arg(Arg::with_name("image") 19 | .short("i") 20 | .long("image") 21 | .value_name("FILE") 22 | .help("Input image filepath") 23 | .conflicts_with("gif") 24 | .takes_value(true)) 25 | .arg(Arg::with_name("gif") 26 | .short("g") 27 | .long("gif") 28 | .value_name("FILE") 29 | .help("Input animated gif filepath") 30 | .takes_value(true)) 31 | .get_matches(); 32 | 33 | let image_filepath = matches.value_of("image"); 34 | let gif_filepath = matches.value_of("gif"); 35 | 36 | let size = terminal_size(); 37 | 38 | if let Some((Width(w), Height(h))) = size { 39 | if let Some(filepath) = image_filepath { 40 | display_image(filepath, w as u32, h as u32); 41 | } else if let Some(filepath) = gif_filepath { 42 | loop { 43 | display_gif(filepath, w as u32, h as u32); 44 | 45 | } 46 | } 47 | } else { 48 | println!("Error: Unable to get terminal dimensions."); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate image; 2 | extern crate terminal_graphics; 3 | 4 | use terminal_graphics::Display; 5 | use terminal_graphics::Colour; 6 | 7 | use std::path::Path; 8 | use std::fs::File; 9 | use std::{thread, time}; 10 | 11 | use image::{ConvertBuffer, GenericImage, Pixel, RgbImage, gif, Frames, ImageDecoder}; 12 | 13 | pub fn pixelate_image(img: &mut RgbImage, width: u32, height: u32) -> RgbImage { 14 | // Create a new ImgBuf 15 | let mut imgbuf = image::ImageBuffer::new(width, height); 16 | 17 | closest_match(&mut imgbuf, img); 18 | 19 | imgbuf 20 | } 21 | 22 | /// Opens the file and reads a gif animated image. 23 | /// Returns the Frames for the gif. 24 | pub fn get_gif(filename: &str) -> Frames { 25 | let f = File::open(filename).expect("File not found"); 26 | let decoder = gif::Decoder::new(f); 27 | 28 | decoder.into_frames().expect("error decoding gif") 29 | } 30 | 31 | pub fn get_image(filename: &str) -> RgbImage { 32 | let img = image::open(&Path::new(filename)).unwrap(); 33 | 34 | img.to_rgb() 35 | } 36 | 37 | /// Downsizes the image using the closest match algorithm 38 | pub fn closest_match(imgbuf: &mut RgbImage, img: &mut RgbImage) { 39 | // The dimensions method returns the images width and height 40 | let (width, height) = img.dimensions(); 41 | let (out_width, out_height) = imgbuf.dimensions(); 42 | 43 | // set the variables needed to average out the pixels 44 | let scale_x = (width as f32)/(out_width as f32); 45 | let scale_y = (height as f32)/(out_height as f32); 46 | 47 | // Iterate over the coordinates and pixels of the image 48 | for (x, y, pixel) in imgbuf.enumerate_pixels_mut() { 49 | let x_f32 = x as f32; 50 | let y_f32 = y as f32; 51 | 52 | let closest_pixel_x = (x_f32 * scale_x).floor() as u32; 53 | let closest_pixel_y = (y_f32 * scale_y).floor() as u32; 54 | 55 | let closest_pixel = img.get_pixel(closest_pixel_x, closest_pixel_y); 56 | 57 | // Create an 8bit pixel of type RGB 58 | *pixel = closest_pixel.clone(); 59 | } 60 | } 61 | 62 | /// Averages all rgb values of a group of pixels. Causes dimming. 63 | pub fn merge_pixels(imgbuf: &mut RgbImage, img: &mut RgbImage) { 64 | // The dimensions method returns the images width and height 65 | let (width, height) = img.dimensions(); 66 | let (out_width, out_height) = imgbuf.dimensions(); 67 | 68 | // set the variables needed to average out the pixels 69 | let scale_x = width/out_width; 70 | let scale_y = height/(out_height); 71 | let num_pixels = (scale_y*scale_x) as f32; 72 | 73 | // Iterate over the coordinates and pixels of the image 74 | for (x, y, pixel) in imgbuf.enumerate_pixels_mut() { 75 | 76 | // create a large pixel which will equal the average of all the 77 | // pixels in the sub image 78 | let mut big_pixel_rgb: [f32; 3] = [0.0, 0.0, 0.0]; 79 | 80 | // create the sub image of size equal to the size of the big pixel 81 | let big_pixel = img.sub_image(x*scale_x, y*scale_y, scale_x, scale_y); 82 | 83 | // average the rgb values 84 | for (_, _, pixel) in big_pixel.pixels() { 85 | let rgb_values = pixel.to_rgb().data; 86 | 87 | big_pixel_rgb[0] += rgb_values[0] as f32; 88 | big_pixel_rgb[1] += rgb_values[1] as f32; 89 | big_pixel_rgb[2] += rgb_values[2] as f32; 90 | } 91 | big_pixel_rgb[0] *= 1.0/num_pixels; 92 | big_pixel_rgb[1] *= 1.0/num_pixels; 93 | big_pixel_rgb[2] *= 1.0/num_pixels; 94 | 95 | // Create an 8bit pixel of type RGB 96 | *pixel = image::Rgb([big_pixel_rgb[0] as u8, big_pixel_rgb[1] as u8, big_pixel_rgb[2] as u8]); 97 | } 98 | } 99 | 100 | fn render_image(mut image: RgbImage, width: u32, height: u32) { 101 | // get the image dimensions 102 | let (img_width, img_height) = image.dimensions(); 103 | let ratio = (img_width as f32)/(img_height as f32); 104 | 105 | let mut display_height = height; 106 | let mut display_width = width; 107 | 108 | // scale the image to the correct dimensions 109 | if ratio > 1.00 { 110 | display_height = ((display_width as f32)/(ratio * 2.0)).floor() as u32; 111 | } else { 112 | display_width = 2 * ((display_height as f32) * ratio).floor() as u32; 113 | } 114 | 115 | // scale the image up if it is too small to fit the dimensions of the terminal 116 | let mut scale = 1.00; 117 | if (display_width as f32)*scale > (img_width as f32) { 118 | scale = (img_width as f32)/(width as f32); 119 | } 120 | if (display_height as f32)*scale > (img_height as f32) { 121 | scale = (img_height as f32)/(height as f32); 122 | } 123 | 124 | // scale the width and height 125 | display_width = ((display_width as f32)*scale).floor() as u32; 126 | display_height = ((display_height as f32)*scale).floor() as u32; 127 | 128 | // scale the display to fit the terminal: 129 | scale = 1.0; 130 | if (display_width as f32) * scale > (width as f32) { 131 | scale = (width as f32) / (display_width as f32); 132 | } 133 | if (display_height as f32) * scale > (height as f32) { 134 | scale = (height as f32) / (display_height as f32); 135 | } 136 | 137 | // scale the width and height 138 | display_width = ((display_width as f32)*scale).floor() as u32; 139 | display_height = ((display_height as f32)*scale).floor() as u32; 140 | 141 | let mut screen = Display::new(display_width, display_height); 142 | screen.clear(); 143 | 144 | let img_out = pixelate_image(&mut image, display_width, display_height * 2); 145 | 146 | for (x, y, pixel) in img_out.enumerate_pixels() { 147 | let rgb = pixel.to_rgb().data; 148 | let colour = Colour::from_rgb(rgb[0], rgb[1], rgb[2]); 149 | 150 | match y % 2 { 151 | 0 => screen.set_pixel(x as isize, (y/2) as isize, '▄', colour, colour), 152 | 1 => screen.get_mut_pixel(x as isize, ((y - 1)/2) as isize).set_colour(colour), 153 | _ => println!("That shouldn't happen"), 154 | } 155 | } 156 | 157 | screen.print(); 158 | } 159 | 160 | pub fn display_image(image_filepath: &str, width: u32, height: u32) { 161 | let img = get_image(image_filepath); 162 | 163 | render_image(img, width, height); 164 | println!(""); 165 | } 166 | 167 | pub fn display_gif(gif_filepath: &str, width: u32, height: u32) { 168 | // get the original gif 169 | let frames = get_gif(gif_filepath); 170 | 171 | let mut modified_frames = Vec::new(); 172 | 173 | // create the new reduced gif by shrinking each frame to fit 174 | // the terminal 175 | for frame in frames { 176 | let delay = frame.delay().to_integer() as u64; 177 | let image = frame.into_buffer(); 178 | 179 | modified_frames.push((image.clone(), delay)); 180 | 181 | // display the image: 182 | render_image(image.convert(), width, height); 183 | 184 | thread::sleep(time::Duration::from_millis(delay)); 185 | } 186 | 187 | loop { 188 | for (frame, delay) in modified_frames.clone() { 189 | render_image(frame.clone().convert(), width, height); 190 | thread::sleep(time::Duration::from_millis(delay)); 191 | } 192 | } 193 | } 194 | --------------------------------------------------------------------------------