├── .github └── workflows │ └── Build.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── example_output.png ├── rustfmt.toml └── src ├── export.rs └── main.rs /.github/workflows/Build.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Build 20 | run: cargo build --verbose 21 | - name: Run tests 22 | run: cargo test --verbose 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Image data 2 | *.jpg 3 | *.png 4 | *.jpeg 5 | *.html 6 | !example_output.png 7 | 8 | # Output 9 | palette.* 10 | 11 | # Generated by Cargo 12 | # will have compiled files and executables 13 | debug/ 14 | target/ 15 | 16 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 17 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 18 | # Cargo.lock 19 | 20 | # These are backup files generated by rustfmt 21 | **/*.rs.bk 22 | 23 | # MSVC Windows builds of rustc generate these, which store debugging information 24 | *.pdb 25 | 26 | .vscode 27 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "adler" 7 | version = "1.0.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 10 | 11 | [[package]] 12 | name = "adler32" 13 | version = "1.2.0" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" 16 | 17 | [[package]] 18 | name = "atty" 19 | version = "0.2.14" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 22 | dependencies = [ 23 | "hermit-abi", 24 | "libc", 25 | "winapi", 26 | ] 27 | 28 | [[package]] 29 | name = "autocfg" 30 | version = "1.1.0" 31 | source = "registry+https://github.com/rust-lang/crates.io-index" 32 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 33 | 34 | [[package]] 35 | name = "bit_field" 36 | version = "0.10.1" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4" 39 | 40 | [[package]] 41 | name = "bitflags" 42 | version = "1.3.2" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 45 | 46 | [[package]] 47 | name = "bumpalo" 48 | version = "3.10.0" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3" 51 | 52 | [[package]] 53 | name = "bytemuck" 54 | version = "1.11.0" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | checksum = "a5377c8865e74a160d21f29c2d40669f53286db6eab59b88540cbb12ffc8b835" 57 | 58 | [[package]] 59 | name = "byteorder" 60 | version = "1.4.3" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 63 | 64 | [[package]] 65 | name = "cfg-if" 66 | version = "1.0.0" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 69 | 70 | [[package]] 71 | name = "clap" 72 | version = "3.2.19" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "68d43934757334b5c0519ff882e1ab9647ac0258b47c24c4f490d78e42697fd5" 75 | dependencies = [ 76 | "atty", 77 | "bitflags", 78 | "clap_derive", 79 | "clap_lex", 80 | "indexmap", 81 | "once_cell", 82 | "strsim", 83 | "termcolor", 84 | "textwrap", 85 | ] 86 | 87 | [[package]] 88 | name = "clap_derive" 89 | version = "3.2.18" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" 92 | dependencies = [ 93 | "heck", 94 | "proc-macro-error", 95 | "proc-macro2", 96 | "quote", 97 | "syn", 98 | ] 99 | 100 | [[package]] 101 | name = "clap_lex" 102 | version = "0.2.4" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" 105 | dependencies = [ 106 | "os_str_bytes", 107 | ] 108 | 109 | [[package]] 110 | name = "cliris" 111 | version = "0.2.0" 112 | dependencies = [ 113 | "clap", 114 | "iris-lib", 115 | ] 116 | 117 | [[package]] 118 | name = "color_quant" 119 | version = "1.1.0" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" 122 | 123 | [[package]] 124 | name = "crc32fast" 125 | version = "1.3.2" 126 | source = "registry+https://github.com/rust-lang/crates.io-index" 127 | checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" 128 | dependencies = [ 129 | "cfg-if", 130 | ] 131 | 132 | [[package]] 133 | name = "crossbeam-channel" 134 | version = "0.5.6" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" 137 | dependencies = [ 138 | "cfg-if", 139 | "crossbeam-utils", 140 | ] 141 | 142 | [[package]] 143 | name = "crossbeam-deque" 144 | version = "0.8.2" 145 | source = "registry+https://github.com/rust-lang/crates.io-index" 146 | checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" 147 | dependencies = [ 148 | "cfg-if", 149 | "crossbeam-epoch", 150 | "crossbeam-utils", 151 | ] 152 | 153 | [[package]] 154 | name = "crossbeam-epoch" 155 | version = "0.9.10" 156 | source = "registry+https://github.com/rust-lang/crates.io-index" 157 | checksum = "045ebe27666471bb549370b4b0b3e51b07f56325befa4284db65fc89c02511b1" 158 | dependencies = [ 159 | "autocfg", 160 | "cfg-if", 161 | "crossbeam-utils", 162 | "memoffset", 163 | "once_cell", 164 | "scopeguard", 165 | ] 166 | 167 | [[package]] 168 | name = "crossbeam-utils" 169 | version = "0.8.11" 170 | source = "registry+https://github.com/rust-lang/crates.io-index" 171 | checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc" 172 | dependencies = [ 173 | "cfg-if", 174 | "once_cell", 175 | ] 176 | 177 | [[package]] 178 | name = "deflate" 179 | version = "1.0.0" 180 | source = "registry+https://github.com/rust-lang/crates.io-index" 181 | checksum = "c86f7e25f518f4b81808a2cf1c50996a61f5c2eb394b2393bd87f2a4780a432f" 182 | dependencies = [ 183 | "adler32", 184 | ] 185 | 186 | [[package]] 187 | name = "either" 188 | version = "1.7.0" 189 | source = "registry+https://github.com/rust-lang/crates.io-index" 190 | checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" 191 | 192 | [[package]] 193 | name = "exr" 194 | version = "1.4.2" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | checksum = "14cc0e06fb5f67e5d6beadf3a382fec9baca1aa751c6d5368fdeee7e5932c215" 197 | dependencies = [ 198 | "bit_field", 199 | "deflate", 200 | "flume", 201 | "half", 202 | "inflate", 203 | "lebe", 204 | "smallvec", 205 | "threadpool", 206 | ] 207 | 208 | [[package]] 209 | name = "flate2" 210 | version = "1.0.24" 211 | source = "registry+https://github.com/rust-lang/crates.io-index" 212 | checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" 213 | dependencies = [ 214 | "crc32fast", 215 | "miniz_oxide", 216 | ] 217 | 218 | [[package]] 219 | name = "flume" 220 | version = "0.10.14" 221 | source = "registry+https://github.com/rust-lang/crates.io-index" 222 | checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577" 223 | dependencies = [ 224 | "futures-core", 225 | "futures-sink", 226 | "nanorand", 227 | "pin-project", 228 | "spin", 229 | ] 230 | 231 | [[package]] 232 | name = "futures-core" 233 | version = "0.3.21" 234 | source = "registry+https://github.com/rust-lang/crates.io-index" 235 | checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" 236 | 237 | [[package]] 238 | name = "futures-sink" 239 | version = "0.3.21" 240 | source = "registry+https://github.com/rust-lang/crates.io-index" 241 | checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" 242 | 243 | [[package]] 244 | name = "getrandom" 245 | version = "0.2.7" 246 | source = "registry+https://github.com/rust-lang/crates.io-index" 247 | checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" 248 | dependencies = [ 249 | "cfg-if", 250 | "js-sys", 251 | "libc", 252 | "wasi", 253 | "wasm-bindgen", 254 | ] 255 | 256 | [[package]] 257 | name = "gif" 258 | version = "0.11.4" 259 | source = "registry+https://github.com/rust-lang/crates.io-index" 260 | checksum = "3edd93c6756b4dfaf2709eafcc345ba2636565295c198a9cfbf75fa5e3e00b06" 261 | dependencies = [ 262 | "color_quant", 263 | "weezl", 264 | ] 265 | 266 | [[package]] 267 | name = "half" 268 | version = "1.8.2" 269 | source = "registry+https://github.com/rust-lang/crates.io-index" 270 | checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" 271 | 272 | [[package]] 273 | name = "hashbrown" 274 | version = "0.12.3" 275 | source = "registry+https://github.com/rust-lang/crates.io-index" 276 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 277 | 278 | [[package]] 279 | name = "heck" 280 | version = "0.4.0" 281 | source = "registry+https://github.com/rust-lang/crates.io-index" 282 | checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" 283 | 284 | [[package]] 285 | name = "hermit-abi" 286 | version = "0.1.19" 287 | source = "registry+https://github.com/rust-lang/crates.io-index" 288 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 289 | dependencies = [ 290 | "libc", 291 | ] 292 | 293 | [[package]] 294 | name = "image" 295 | version = "0.24.3" 296 | source = "registry+https://github.com/rust-lang/crates.io-index" 297 | checksum = "7e30ca2ecf7666107ff827a8e481de6a132a9b687ed3bb20bb1c144a36c00964" 298 | dependencies = [ 299 | "bytemuck", 300 | "byteorder", 301 | "color_quant", 302 | "exr", 303 | "gif", 304 | "jpeg-decoder", 305 | "num-rational", 306 | "num-traits", 307 | "png", 308 | "scoped_threadpool", 309 | "tiff", 310 | ] 311 | 312 | [[package]] 313 | name = "indexmap" 314 | version = "1.9.1" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" 317 | dependencies = [ 318 | "autocfg", 319 | "hashbrown", 320 | ] 321 | 322 | [[package]] 323 | name = "inflate" 324 | version = "0.4.5" 325 | source = "registry+https://github.com/rust-lang/crates.io-index" 326 | checksum = "1cdb29978cc5797bd8dcc8e5bf7de604891df2a8dc576973d71a281e916db2ff" 327 | dependencies = [ 328 | "adler32", 329 | ] 330 | 331 | [[package]] 332 | name = "iris-lib" 333 | version = "0.1.0" 334 | source = "registry+https://github.com/rust-lang/crates.io-index" 335 | checksum = "2c29ff60ef55de609e001addb5d9ead10956f7f0dae1b9768d1826bfe2fa08b2" 336 | dependencies = [ 337 | "image", 338 | ] 339 | 340 | [[package]] 341 | name = "jpeg-decoder" 342 | version = "0.2.6" 343 | source = "registry+https://github.com/rust-lang/crates.io-index" 344 | checksum = "9478aa10f73e7528198d75109c8be5cd7d15fb530238040148d5f9a22d4c5b3b" 345 | dependencies = [ 346 | "rayon", 347 | ] 348 | 349 | [[package]] 350 | name = "js-sys" 351 | version = "0.3.59" 352 | source = "registry+https://github.com/rust-lang/crates.io-index" 353 | checksum = "258451ab10b34f8af53416d1fdab72c22e805f0c92a1136d59470ec0b11138b2" 354 | dependencies = [ 355 | "wasm-bindgen", 356 | ] 357 | 358 | [[package]] 359 | name = "lebe" 360 | version = "0.5.1" 361 | source = "registry+https://github.com/rust-lang/crates.io-index" 362 | checksum = "7efd1d698db0759e6ef11a7cd44407407399a910c774dd804c64c032da7826ff" 363 | 364 | [[package]] 365 | name = "libc" 366 | version = "0.2.127" 367 | source = "registry+https://github.com/rust-lang/crates.io-index" 368 | checksum = "505e71a4706fa491e9b1b55f51b95d4037d0821ee40131190475f692b35b009b" 369 | 370 | [[package]] 371 | name = "lock_api" 372 | version = "0.4.7" 373 | source = "registry+https://github.com/rust-lang/crates.io-index" 374 | checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" 375 | dependencies = [ 376 | "autocfg", 377 | "scopeguard", 378 | ] 379 | 380 | [[package]] 381 | name = "log" 382 | version = "0.4.17" 383 | source = "registry+https://github.com/rust-lang/crates.io-index" 384 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 385 | dependencies = [ 386 | "cfg-if", 387 | ] 388 | 389 | [[package]] 390 | name = "memoffset" 391 | version = "0.6.5" 392 | source = "registry+https://github.com/rust-lang/crates.io-index" 393 | checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" 394 | dependencies = [ 395 | "autocfg", 396 | ] 397 | 398 | [[package]] 399 | name = "miniz_oxide" 400 | version = "0.5.3" 401 | source = "registry+https://github.com/rust-lang/crates.io-index" 402 | checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc" 403 | dependencies = [ 404 | "adler", 405 | ] 406 | 407 | [[package]] 408 | name = "nanorand" 409 | version = "0.7.0" 410 | source = "registry+https://github.com/rust-lang/crates.io-index" 411 | checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" 412 | dependencies = [ 413 | "getrandom", 414 | ] 415 | 416 | [[package]] 417 | name = "num-integer" 418 | version = "0.1.45" 419 | source = "registry+https://github.com/rust-lang/crates.io-index" 420 | checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" 421 | dependencies = [ 422 | "autocfg", 423 | "num-traits", 424 | ] 425 | 426 | [[package]] 427 | name = "num-rational" 428 | version = "0.4.1" 429 | source = "registry+https://github.com/rust-lang/crates.io-index" 430 | checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" 431 | dependencies = [ 432 | "autocfg", 433 | "num-integer", 434 | "num-traits", 435 | ] 436 | 437 | [[package]] 438 | name = "num-traits" 439 | version = "0.2.15" 440 | source = "registry+https://github.com/rust-lang/crates.io-index" 441 | checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" 442 | dependencies = [ 443 | "autocfg", 444 | ] 445 | 446 | [[package]] 447 | name = "num_cpus" 448 | version = "1.13.1" 449 | source = "registry+https://github.com/rust-lang/crates.io-index" 450 | checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" 451 | dependencies = [ 452 | "hermit-abi", 453 | "libc", 454 | ] 455 | 456 | [[package]] 457 | name = "once_cell" 458 | version = "1.13.0" 459 | source = "registry+https://github.com/rust-lang/crates.io-index" 460 | checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" 461 | 462 | [[package]] 463 | name = "os_str_bytes" 464 | version = "6.2.0" 465 | source = "registry+https://github.com/rust-lang/crates.io-index" 466 | checksum = "648001efe5d5c0102d8cea768e348da85d90af8ba91f0bea908f157951493cd4" 467 | 468 | [[package]] 469 | name = "pin-project" 470 | version = "1.0.11" 471 | source = "registry+https://github.com/rust-lang/crates.io-index" 472 | checksum = "78203e83c48cffbe01e4a2d35d566ca4de445d79a85372fc64e378bfc812a260" 473 | dependencies = [ 474 | "pin-project-internal", 475 | ] 476 | 477 | [[package]] 478 | name = "pin-project-internal" 479 | version = "1.0.11" 480 | source = "registry+https://github.com/rust-lang/crates.io-index" 481 | checksum = "710faf75e1b33345361201d36d04e98ac1ed8909151a017ed384700836104c74" 482 | dependencies = [ 483 | "proc-macro2", 484 | "quote", 485 | "syn", 486 | ] 487 | 488 | [[package]] 489 | name = "png" 490 | version = "0.17.5" 491 | source = "registry+https://github.com/rust-lang/crates.io-index" 492 | checksum = "dc38c0ad57efb786dd57b9864e5b18bae478c00c824dc55a38bbc9da95dde3ba" 493 | dependencies = [ 494 | "bitflags", 495 | "crc32fast", 496 | "deflate", 497 | "miniz_oxide", 498 | ] 499 | 500 | [[package]] 501 | name = "proc-macro-error" 502 | version = "1.0.4" 503 | source = "registry+https://github.com/rust-lang/crates.io-index" 504 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 505 | dependencies = [ 506 | "proc-macro-error-attr", 507 | "proc-macro2", 508 | "quote", 509 | "syn", 510 | "version_check", 511 | ] 512 | 513 | [[package]] 514 | name = "proc-macro-error-attr" 515 | version = "1.0.4" 516 | source = "registry+https://github.com/rust-lang/crates.io-index" 517 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 518 | dependencies = [ 519 | "proc-macro2", 520 | "quote", 521 | "version_check", 522 | ] 523 | 524 | [[package]] 525 | name = "proc-macro2" 526 | version = "1.0.43" 527 | source = "registry+https://github.com/rust-lang/crates.io-index" 528 | checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" 529 | dependencies = [ 530 | "unicode-ident", 531 | ] 532 | 533 | [[package]] 534 | name = "quote" 535 | version = "1.0.21" 536 | source = "registry+https://github.com/rust-lang/crates.io-index" 537 | checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" 538 | dependencies = [ 539 | "proc-macro2", 540 | ] 541 | 542 | [[package]] 543 | name = "rayon" 544 | version = "1.5.3" 545 | source = "registry+https://github.com/rust-lang/crates.io-index" 546 | checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d" 547 | dependencies = [ 548 | "autocfg", 549 | "crossbeam-deque", 550 | "either", 551 | "rayon-core", 552 | ] 553 | 554 | [[package]] 555 | name = "rayon-core" 556 | version = "1.9.3" 557 | source = "registry+https://github.com/rust-lang/crates.io-index" 558 | checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" 559 | dependencies = [ 560 | "crossbeam-channel", 561 | "crossbeam-deque", 562 | "crossbeam-utils", 563 | "num_cpus", 564 | ] 565 | 566 | [[package]] 567 | name = "scoped_threadpool" 568 | version = "0.1.9" 569 | source = "registry+https://github.com/rust-lang/crates.io-index" 570 | checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" 571 | 572 | [[package]] 573 | name = "scopeguard" 574 | version = "1.1.0" 575 | source = "registry+https://github.com/rust-lang/crates.io-index" 576 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 577 | 578 | [[package]] 579 | name = "smallvec" 580 | version = "1.9.0" 581 | source = "registry+https://github.com/rust-lang/crates.io-index" 582 | checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" 583 | 584 | [[package]] 585 | name = "spin" 586 | version = "0.9.4" 587 | source = "registry+https://github.com/rust-lang/crates.io-index" 588 | checksum = "7f6002a767bff9e83f8eeecf883ecb8011875a21ae8da43bffb817a57e78cc09" 589 | dependencies = [ 590 | "lock_api", 591 | ] 592 | 593 | [[package]] 594 | name = "strsim" 595 | version = "0.10.0" 596 | source = "registry+https://github.com/rust-lang/crates.io-index" 597 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 598 | 599 | [[package]] 600 | name = "syn" 601 | version = "1.0.99" 602 | source = "registry+https://github.com/rust-lang/crates.io-index" 603 | checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" 604 | dependencies = [ 605 | "proc-macro2", 606 | "quote", 607 | "unicode-ident", 608 | ] 609 | 610 | [[package]] 611 | name = "termcolor" 612 | version = "1.1.3" 613 | source = "registry+https://github.com/rust-lang/crates.io-index" 614 | checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" 615 | dependencies = [ 616 | "winapi-util", 617 | ] 618 | 619 | [[package]] 620 | name = "textwrap" 621 | version = "0.15.0" 622 | source = "registry+https://github.com/rust-lang/crates.io-index" 623 | checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" 624 | 625 | [[package]] 626 | name = "threadpool" 627 | version = "1.8.1" 628 | source = "registry+https://github.com/rust-lang/crates.io-index" 629 | checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" 630 | dependencies = [ 631 | "num_cpus", 632 | ] 633 | 634 | [[package]] 635 | name = "tiff" 636 | version = "0.7.3" 637 | source = "registry+https://github.com/rust-lang/crates.io-index" 638 | checksum = "7259662e32d1e219321eb309d5f9d898b779769d81b76e762c07c8e5d38fcb65" 639 | dependencies = [ 640 | "flate2", 641 | "jpeg-decoder", 642 | "weezl", 643 | ] 644 | 645 | [[package]] 646 | name = "unicode-ident" 647 | version = "1.0.3" 648 | source = "registry+https://github.com/rust-lang/crates.io-index" 649 | checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf" 650 | 651 | [[package]] 652 | name = "version_check" 653 | version = "0.9.4" 654 | source = "registry+https://github.com/rust-lang/crates.io-index" 655 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 656 | 657 | [[package]] 658 | name = "wasi" 659 | version = "0.11.0+wasi-snapshot-preview1" 660 | source = "registry+https://github.com/rust-lang/crates.io-index" 661 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 662 | 663 | [[package]] 664 | name = "wasm-bindgen" 665 | version = "0.2.82" 666 | source = "registry+https://github.com/rust-lang/crates.io-index" 667 | checksum = "fc7652e3f6c4706c8d9cd54832c4a4ccb9b5336e2c3bd154d5cccfbf1c1f5f7d" 668 | dependencies = [ 669 | "cfg-if", 670 | "wasm-bindgen-macro", 671 | ] 672 | 673 | [[package]] 674 | name = "wasm-bindgen-backend" 675 | version = "0.2.82" 676 | source = "registry+https://github.com/rust-lang/crates.io-index" 677 | checksum = "662cd44805586bd52971b9586b1df85cdbbd9112e4ef4d8f41559c334dc6ac3f" 678 | dependencies = [ 679 | "bumpalo", 680 | "log", 681 | "once_cell", 682 | "proc-macro2", 683 | "quote", 684 | "syn", 685 | "wasm-bindgen-shared", 686 | ] 687 | 688 | [[package]] 689 | name = "wasm-bindgen-macro" 690 | version = "0.2.82" 691 | source = "registry+https://github.com/rust-lang/crates.io-index" 692 | checksum = "b260f13d3012071dfb1512849c033b1925038373aea48ced3012c09df952c602" 693 | dependencies = [ 694 | "quote", 695 | "wasm-bindgen-macro-support", 696 | ] 697 | 698 | [[package]] 699 | name = "wasm-bindgen-macro-support" 700 | version = "0.2.82" 701 | source = "registry+https://github.com/rust-lang/crates.io-index" 702 | checksum = "5be8e654bdd9b79216c2929ab90721aa82faf65c48cdf08bdc4e7f51357b80da" 703 | dependencies = [ 704 | "proc-macro2", 705 | "quote", 706 | "syn", 707 | "wasm-bindgen-backend", 708 | "wasm-bindgen-shared", 709 | ] 710 | 711 | [[package]] 712 | name = "wasm-bindgen-shared" 713 | version = "0.2.82" 714 | source = "registry+https://github.com/rust-lang/crates.io-index" 715 | checksum = "6598dd0bd3c7d51095ff6531a5b23e02acdc81804e30d8f07afb77b7215a140a" 716 | 717 | [[package]] 718 | name = "weezl" 719 | version = "0.1.7" 720 | source = "registry+https://github.com/rust-lang/crates.io-index" 721 | checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" 722 | 723 | [[package]] 724 | name = "winapi" 725 | version = "0.3.9" 726 | source = "registry+https://github.com/rust-lang/crates.io-index" 727 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 728 | dependencies = [ 729 | "winapi-i686-pc-windows-gnu", 730 | "winapi-x86_64-pc-windows-gnu", 731 | ] 732 | 733 | [[package]] 734 | name = "winapi-i686-pc-windows-gnu" 735 | version = "0.4.0" 736 | source = "registry+https://github.com/rust-lang/crates.io-index" 737 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 738 | 739 | [[package]] 740 | name = "winapi-util" 741 | version = "0.1.5" 742 | source = "registry+https://github.com/rust-lang/crates.io-index" 743 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 744 | dependencies = [ 745 | "winapi", 746 | ] 747 | 748 | [[package]] 749 | name = "winapi-x86_64-pc-windows-gnu" 750 | version = "0.4.0" 751 | source = "registry+https://github.com/rust-lang/crates.io-index" 752 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 753 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cliris" 3 | version = "0.2.0" 4 | edition = "2018" 5 | authors = ["Andrej G."] 6 | license = "MIT" 7 | description = "A cli tool that creates color palettes from images using the median cut algorithm." 8 | readme = "README.md" 9 | homepage = "https://github.com/Kaesebrot84/iris" 10 | repository = "https://github.com/Kaesebrot84/iris" 11 | keywords = ["cli", "image", "color", "quantization"] 12 | categories = ["command-line-utilities"] 13 | 14 | [dependencies] 15 | clap = { version = "3.2.19", features = ["derive"] } 16 | iris-lib = { version = "0.1.0", features = ["image"] } 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022 Andrej G. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Iris 2 | 3 | [![Rust](https://github.com/Kaesebrot84/iris/actions/workflows/Build.yml/badge.svg)](https://github.com/Kaesebrot84/iris/actions/workflows/Build.yml) 4 | [![crates.io](https://img.shields.io/crates/v/iris-lib.svg)](https://crates.io/crates/cliris) 5 | 6 | A command line tool that creates color palettes from images using the [median 7 | cut algorithm](https://en.wikipedia.org/wiki/Median_cut). 8 | 9 | ## Usage 10 | 11 | ```sh 12 | $ cliris --help 13 | cliris 0.2.0 14 | Andrej G. 15 | A cli tool that creates color palettes from images using the median cut algorithm. 16 | 17 | USAGE: 18 | cliris [OPTIONS] --file-name [OUTPUT_FORMAT] 19 | 20 | ARGS: 21 | Desired data file format to be written [default: none] [possible values: 22 | none, html, json, csv] 23 | 24 | OPTIONS: 25 | -f, --file-name Target image file name 26 | -h, --help Print help information 27 | -i, --iterations Number of iterations [default: 1] 28 | -o, --out-filename File path the file should be written to [default: palette] 29 | -V, --version Print version information 30 | ``` 31 | 32 | ### Installation 33 | 34 | ```bash 35 | cargo install cliris 36 | ``` 37 | 38 | ### Example 39 | 40 | ```sh 41 | $ cliris -f peppers.png -i 3 html 42 | 43 | Generating palette... 44 | Finished generating palette in 75 ms. 45 | 46 | { R: 191, G: 207, B: 141, A: 255 } 47 | { R: 139, G: 187, B: 108, A: 255 } 48 | { R: 171, G: 185, B: 76, A: 255 } 49 | { R: 118, G: 159, B: 71, A: 255 } 50 | { R: 197, G: 60, B: 50, A: 255 } 51 | { R: 186, G: 41, B: 34, A: 255 } 52 | { R: 117, G: 77, B: 45, A: 255 } 53 | { R: 78, G: 7, B: 6, A: 255 } 54 | ``` 55 | 56 |

57 | example_output_image 58 |

59 | 60 | ### Library 61 | 62 | This project uses the [iris-lib](https://crates.io/crates/iris-lib) crate, which performs this algorithm as a stand-alone library. 63 | -------------------------------------------------------------------------------- /example_output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kaesebrot84/iris/61b749d180851ccb8cc255821b0ec26ab0c294d6/example_output.png -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 200 -------------------------------------------------------------------------------- /src/export.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use std::io::Write; 3 | 4 | use iris_lib::color::Color; 5 | 6 | /// Writes a html file containing the target image and the according colors. 7 | /// 8 | /// TODO: Refactor 9 | /// - If output file is not in the same folder as image, image is not displayed. Write entire image path intp html instead. 10 | /// 11 | /// # Arguments 12 | /// 13 | /// * `image_file_path` - Image path to be included as `src` in the `html` file. 14 | /// * `color_data` - Colors to be included as "tiles" in the html file. 15 | /// * `out_file_path` - Path the output file should be written to. 16 | /// 17 | /// # Examples 18 | /// 19 | /// ``` 20 | /// let colors = vec![Color {r: 255, g: 0, b: 0, a: 255}]; 21 | /// write_html_out("example.jpg", &colors, "palette")?; 22 | /// ``` 23 | /// 24 | pub fn write_html_out(image_file_path: &str, color_data: &[Color], out_file_path: &str) -> std::io::Result<()> { 25 | let mut html: String = String::new(); 26 | 27 | html.push_str(""); 28 | html.push_str(""); 29 | html.push_str(""); 30 | html.push_str(""); 31 | html.push_str(""); 32 | html.push_str(""); 33 | let image_element = format!("\"Input", image_file_path); 34 | html.push_str(image_element.as_str()); 35 | html.push_str(""); 36 | html.push_str("
"); 37 | 38 | for c in color_data { 39 | let color_element = format!( 40 | "
", 41 | c.r, c.g, c.b 42 | ); 43 | html.push_str(color_element.as_str()); 44 | } 45 | 46 | html.push_str("
"); 47 | html.push_str(""); 48 | 49 | let mut file = File::create(format!("{}.html", out_file_path))?; 50 | file.write_all(html.as_bytes())?; 51 | Ok(()) 52 | } 53 | 54 | /// Writes a color palette to a json file. 55 | /// 56 | /// # Arguments 57 | /// 58 | /// * `color_data` - Colors to be written as a palette to json. 59 | /// * `out_file_path` - Path the html file should be written to. 60 | /// 61 | /// # Examples 62 | /// 63 | /// ``` 64 | /// let colors = vec![Color {r: 255, g: 0, b: 0, a: 255}]; 65 | /// write_json_out(&colors, "palette")?; 66 | /// ``` 67 | /// 68 | pub fn write_json_out(color_data: &[Color], out_file_path: &str) -> std::io::Result<()> { 69 | let mut json: String = String::new(); 70 | json.push('{'); 71 | json.push_str("\"palette\": ["); 72 | 73 | let mut color_it = color_data.iter().peekable(); 74 | 75 | while let Some(color) = color_it.next() { 76 | json.push_str(format!("{{ \"r\": {}, \"g\": {}, \"b\": {}, \"a\": {} }}", color.r, color.g, color.b, color.a).as_str()); 77 | if color_it.peek().is_some() { 78 | json.push(','); 79 | } 80 | } 81 | json.push_str("]}"); 82 | 83 | let mut file = File::create(format!("{}.json", out_file_path))?; 84 | file.write_all(json.as_bytes())?; 85 | 86 | Ok(()) 87 | } 88 | 89 | /// Writes a color palette to a csv file. 90 | /// First row in the file will be headers R, G, B, A. 91 | /// 92 | /// # Arguments 93 | /// 94 | /// * `color_data` - Colors to be written to the csv file. 95 | /// * `out_file_path` - Path the output file should be written to. 96 | /// 97 | /// # Examples 98 | /// 99 | /// ``` 100 | /// let colors = vec![Color {r: 255, g: 0, b: 0, a: 255}]; 101 | /// write_csv_out(&colors, "palette")?; 102 | /// ``` 103 | /// 104 | pub fn write_csv_out(color_data: &[Color], out_file_path: &str) -> std::io::Result<()> { 105 | let mut csv: String = String::new(); 106 | 107 | csv.push_str("R, G, B, A\n"); 108 | 109 | for color in color_data { 110 | csv.push_str(format!("{}, {}, {}, {}\n", color.r, color.g, color.b, color.a).as_str()); 111 | } 112 | 113 | let mut file = File::create(format!("{}.csv", out_file_path))?; 114 | file.write_all(csv.as_bytes())?; 115 | Ok(()) 116 | } 117 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate iris_lib; 2 | 3 | use iris_lib::color_bucket::ColorBucket; 4 | use std::time::Instant; 5 | 6 | use crate::export::*; 7 | 8 | use clap::{ArgEnum, Parser}; 9 | 10 | pub mod export; 11 | 12 | /// A command line tool that creates color palettes from images using the median cut algorithm. 13 | #[derive(Parser, Debug)] 14 | #[clap(author, version, about, long_about = None)] 15 | struct Args { 16 | /// Target image file name. 17 | #[clap(short, long)] 18 | file_name: String, 19 | 20 | /// Number of iterations. 21 | #[clap(short, long, default_value_t = 1)] 22 | iterations: u8, 23 | 24 | /// Desired data file format to be written. 25 | #[clap(arg_enum, default_value_t = OutputFormat::None)] 26 | output_format: OutputFormat, 27 | 28 | /// File path the file should be written to. 29 | #[clap(short, long, default_value_t = String::from("palette"))] 30 | out_filename: String, 31 | } 32 | 33 | /// Represents all possible file output formats for color palettes. 34 | #[derive(ArgEnum, Clone, Debug)] 35 | enum OutputFormat { 36 | None, 37 | Html, 38 | Json, 39 | Csv, 40 | } 41 | 42 | fn main() { 43 | let args = Args::parse(); 44 | 45 | let num_iterations = match args.iterations { 46 | a if a > 4 => { 47 | println!("Switching to maximum number of iterations of 4."); 48 | 4 49 | } 50 | a if a < 1 => { 51 | println!("Switching to minimum number of iterations"); 52 | 1 53 | } 54 | a => a, 55 | }; 56 | 57 | let now = Instant::now(); 58 | println!("Generating palette..."); 59 | 60 | if let Some(mut color_bucket) = ColorBucket::from_image(&args.file_name) { 61 | let palette = color_bucket.make_palette(num_iterations); 62 | println!("Finished generating palette in {} ms.\n", now.elapsed().as_millis()); 63 | 64 | for color in &palette { 65 | println!("{}", color); 66 | } 67 | 68 | match args.output_format { 69 | OutputFormat::Html => match write_html_out(&args.file_name, &palette, &args.out_filename) { 70 | Ok(_) => (), 71 | Err(err) => println!("Failed writing html output file:\n{}", err), 72 | }, 73 | OutputFormat::Json => match write_json_out(&palette, &args.out_filename) { 74 | Ok(_) => (), 75 | Err(err) => println!("Failed writing json output file:\n{}", err), 76 | }, 77 | OutputFormat::Csv => match write_csv_out(&palette, &args.out_filename) { 78 | Ok(_) => (), 79 | Err(err) => println!("Failed writing csv output file:\n{}", err), 80 | }, 81 | OutputFormat::None => (), 82 | } 83 | } 84 | } 85 | --------------------------------------------------------------------------------