mirror of
https://github.com/waveplate/img2irc.git
synced 2024-11-24 16:36:39 +00:00
first commit
This commit is contained in:
commit
8c191f264e
13
Cargo.toml
Normal file
13
Cargo.toml
Normal file
@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "img2irc"
|
||||
version = "0.1.0"
|
||||
authors = ["anatolybazarov"]
|
||||
github = "https://github.com/anatolybazarov/img2irc"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] }
|
||||
reqwest = "0.11.14"
|
||||
photon-rs = "0.3.2"
|
||||
clap = { version = "4.2.0", features = ["cargo", "derive"] }
|
||||
url = "2.3.1"
|
53
README.md
Normal file
53
README.md
Normal file
@ -0,0 +1,53 @@
|
||||
# img2irc (0.1.0)
|
||||
![EVA Loader](https://i.imgur.com/wLyj1HH.png)
|
||||
|
||||
img2irc is a utility which converts images to halfblock irc/ansi art, with a lot of post-processing filters
|
||||
|
||||
*halfblock* means that each row will contain two rows worth of pixels, effectively doubling the resolution
|
||||
|
||||
the `irc` mode has 99 colours, the `ansi` mode has 256
|
||||
|
||||
## usage
|
||||
|
||||
`./img2ansi <URL or PATH> [OPTIONS]`
|
||||
|
||||
| option | description | default value |
|
||||
| ------ | ----------- | ------------- |
|
||||
| `<IMAGE>` | image url or file path | none |
|
||||
| `-r, --render <RENDER>` | render type (irc, ansi) | irc |
|
||||
| `-w, --width <WIDTH>` | output image width in columns | 50 |
|
||||
| `-b, --brightness=<BRIGHTNESS>` | adjust brightness (-255 to 255) | 0 |
|
||||
| `-H, --hue=<HUE>` | adjust hue (-180 to 180) | 0 |
|
||||
| `-c, --contrast=<CONTRAST>` | adjust contrast (-255 to 255) | 0 |
|
||||
| `-s, --saturation=<SATURATION>` | adjust saturation (-255 to 255) | 0 |
|
||||
| `-o, --opacity=<OPACITY>` | adjust opacity (-255 to 255) | 0 |
|
||||
| `-g, --gamma=<GAMMA>` | adjust gamma (-255 to 255) | 0 |
|
||||
| `--dither <DITHER>` | dithering (1 to 8) | 0 |
|
||||
| `--pixelize <PIXELIZE>` | pixelize pixel size | 0 |
|
||||
| `--gaussian-blur <GAUSSIAN_BLUR>` | gaussian blur radius | 0 |
|
||||
| `--oil <OIL>` | oil ("[RADIUS],[INTENSITY]") | |
|
||||
| `--grayscale` | converts image to black and white |
|
||||
| `--halftone` | made up of small dots creating a continuous-tone illusion |
|
||||
| `--sepia` | brownish, aged appearance like old photographs |
|
||||
| `--normalize` | adjusts brightness and contrast for better image quality |
|
||||
| `--noise` | random variations in brightness and color like film grain |
|
||||
| `--emboss` | gives a raised, 3d appearance |
|
||||
| `--box-blur` | smoothed appearance like frosted glass |
|
||||
| `--identity` | no modifications, unchanged image |
|
||||
| `--laplace` | enhances edges and boundaries in an image |
|
||||
| `--noise-reduction` | reduces noise for a cleaner, clearer image |
|
||||
| `--sharpen` | increases clarity and definition, making edges and details more distinct |
|
||||
| `--cali` | cool blue tone with increased contrast |
|
||||
| `--dramatic` | high contrast and vivid colors for a dramatic effect |
|
||||
| `--firenze` | warm, earthy tones reminiscent of tuscan landscapes |
|
||||
| `--golden` | warm, golden glow like sunset light |
|
||||
| `--lix` | high-contrast black and white appearance with increased sharpness |
|
||||
| `--lofi` | low-fidelity, retro appearance like old photographs or film |
|
||||
| `--neue` | clean, modern appearance with neutral colors and simple design |
|
||||
| `--obsidian` | dark, monochromatic appearance with black and gray shades |
|
||||
| `--pastel-pink` | soft, delicate pink tint like pastel colors |
|
||||
| `--ryo` | bright, high-contrast appearance with vivid colors and sharp details |
|
||||
| `--invert` | colors are inverted, opposite on the color wheel |
|
||||
| `--frosted-glass` | blurred, frosted appearance as if viewed through semi-transparent surface |
|
||||
| `--solarize` | strange, otherworldly appearance with inverted colors and surreal atmosphere |
|
||||
| `--edge-detection` | highlights edges and boundaries in an image |
|
161
src/args.rs
Normal file
161
src/args.rs
Normal file
@ -0,0 +1,161 @@
|
||||
use clap::Parser;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
pub struct Args {
|
||||
/// image url or file path
|
||||
#[arg(index = 1)]
|
||||
pub image: String,
|
||||
|
||||
/// render type (irc, ansi)
|
||||
#[arg(short, long)]
|
||||
pub render: Option<String>,
|
||||
|
||||
/// image width to resize to
|
||||
#[arg(short, long, default_value_t = 50)]
|
||||
pub width: u32,
|
||||
|
||||
/// brightness (-255 to 255)
|
||||
#[arg(short, long, require_equals = true, default_value_t = 0)]
|
||||
pub brightness: i16,
|
||||
|
||||
/// hue (-180 to 180)
|
||||
#[arg(short = 'H', long, require_equals = true, default_value_t = 0)]
|
||||
pub hue: i16,
|
||||
|
||||
/// contrast (-255 to 255)
|
||||
#[arg(short, long, require_equals = true, default_value_t = 0)]
|
||||
pub contrast: i16,
|
||||
|
||||
/// saturation (-255 to 255)
|
||||
#[arg(short, long, require_equals = true, default_value_t = 0)]
|
||||
pub saturation: i16,
|
||||
|
||||
/// opacity (-255 to 255)
|
||||
#[arg(short, long, require_equals = true, default_value_t = 0)]
|
||||
pub opacity: i16,
|
||||
|
||||
/// gamma (-255 to 255)
|
||||
#[arg(short, long, require_equals = true, default_value_t = 0)]
|
||||
pub gamma: i16,
|
||||
|
||||
/// dither (1 to 8)
|
||||
#[arg(long, default_value_t = 0)]
|
||||
pub dither: u32,
|
||||
|
||||
/// pixelize size
|
||||
#[arg(long, default_value_t = 0)]
|
||||
pub pixelize: i32,
|
||||
|
||||
/// gaussian blur radius
|
||||
#[arg(long, default_value_t = 0)]
|
||||
pub gaussian_blur: i32,
|
||||
|
||||
/// oil ("<radius>,<intensity>")
|
||||
#[arg(long)]
|
||||
pub oil: Option<String>,
|
||||
|
||||
/// grayscale
|
||||
#[arg(long, default_value_t = false)]
|
||||
pub grayscale: bool,
|
||||
|
||||
/// halftone
|
||||
#[arg(long, default_value_t = false)]
|
||||
pub halftone: bool,
|
||||
|
||||
/// sepia
|
||||
#[arg(long, default_value_t = false)]
|
||||
pub sepia: bool,
|
||||
|
||||
/// normalize
|
||||
#[arg(long, default_value_t = false)]
|
||||
pub normalize: bool,
|
||||
|
||||
/// noise
|
||||
#[arg(long, default_value_t = false)]
|
||||
pub noise: bool,
|
||||
|
||||
/// emboss
|
||||
#[arg(long, default_value_t = false)]
|
||||
pub emboss: bool,
|
||||
|
||||
/// box_blur
|
||||
#[arg(long, default_value_t = false)]
|
||||
pub box_blur: bool,
|
||||
|
||||
/// identity
|
||||
#[arg(long, default_value_t = false)]
|
||||
pub identity: bool,
|
||||
|
||||
/// laplace
|
||||
#[arg(long, default_value_t = false)]
|
||||
pub laplace: bool,
|
||||
|
||||
/// noise reduction
|
||||
#[arg(long, default_value_t = false)]
|
||||
pub noise_reduction: bool,
|
||||
|
||||
/// sharpen
|
||||
#[arg(long, default_value_t = false)]
|
||||
pub sharpen: bool,
|
||||
|
||||
/// cali
|
||||
#[arg(long, default_value_t = false)]
|
||||
pub cali: bool,
|
||||
|
||||
/// dramatic
|
||||
#[arg(long, default_value_t = false)]
|
||||
pub dramatic: bool,
|
||||
|
||||
/// firenze
|
||||
#[arg(long, default_value_t = false)]
|
||||
pub firenze: bool,
|
||||
|
||||
/// golden
|
||||
#[arg(long, default_value_t = false)]
|
||||
pub golden: bool,
|
||||
|
||||
/// lix
|
||||
#[arg(long, default_value_t = false)]
|
||||
pub lix: bool,
|
||||
|
||||
/// lofi
|
||||
#[arg(long, default_value_t = false)]
|
||||
pub lofi: bool,
|
||||
|
||||
/// neue
|
||||
#[arg(long, default_value_t = false)]
|
||||
pub neue: bool,
|
||||
|
||||
/// obsidian
|
||||
#[arg(long, default_value_t = false)]
|
||||
pub obsidian: bool,
|
||||
|
||||
/// pastel_pink
|
||||
#[arg(long, default_value_t = false)]
|
||||
pub pastel_pink: bool,
|
||||
|
||||
/// ryo
|
||||
#[arg(long, default_value_t = false)]
|
||||
pub ryo: bool,
|
||||
|
||||
/// invert
|
||||
#[arg(long, default_value_t = false)]
|
||||
pub invert: bool,
|
||||
|
||||
/// frosted glass
|
||||
#[arg(long, default_value_t = false)]
|
||||
pub frosted_glass: bool,
|
||||
|
||||
/// solarize
|
||||
#[arg(long, default_value_t = false)]
|
||||
pub solarize: bool,
|
||||
|
||||
/// edge detection
|
||||
#[arg(long, default_value_t = false)]
|
||||
pub edge_detection: bool,
|
||||
}
|
||||
|
||||
pub fn parse_args() -> Args {
|
||||
Args::parse()
|
||||
}
|
145
src/draw.rs
Normal file
145
src/draw.rs
Normal file
@ -0,0 +1,145 @@
|
||||
use crate::palette::{RGB99, ANSI256, nearest_hex_color};
|
||||
|
||||
use photon_rs::PhotonImage;
|
||||
|
||||
const CHAR: &str ="\u{2580}";
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AnsiImage {
|
||||
pub image: PhotonImage,
|
||||
pub bitmap: Vec<Vec<u32>>,
|
||||
pub halfblock: Vec<Vec<AnsiPixelPair>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct AnsiPixelPair {
|
||||
pub top: AnsiPixel,
|
||||
pub bottom: AnsiPixel,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct AnsiPixel {
|
||||
pub orig: u32,
|
||||
pub ansi: u8,
|
||||
pub irc: u8,
|
||||
}
|
||||
|
||||
impl AnsiPixel {
|
||||
pub fn new(pixel: &u32) -> AnsiPixel {
|
||||
let irc = nearest_hex_color(*pixel, RGB99.to_vec());
|
||||
let ansi = nearest_hex_color(*pixel, ANSI256.to_vec());
|
||||
AnsiPixel {
|
||||
orig: *pixel,
|
||||
ansi: ansi,
|
||||
irc: irc,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AnsiImage {
|
||||
pub fn new(image: PhotonImage) -> AnsiImage {
|
||||
let mut bitmap = image.get_raw_pixels()
|
||||
.chunks(4)
|
||||
.map(|x| make_rgb(x.to_vec()))
|
||||
.collect::<Vec<u32>>()
|
||||
.chunks(image.get_width() as usize)
|
||||
.map(|x| x.to_vec())
|
||||
.collect::<Vec<Vec<u32>>>();
|
||||
|
||||
if bitmap.len() % 2 != 0 {
|
||||
bitmap.push(vec![0; image.get_width() as usize]);
|
||||
}
|
||||
|
||||
let halfblock = halfblock_bitmap(&bitmap);
|
||||
|
||||
return AnsiImage {
|
||||
image: image,
|
||||
bitmap: bitmap,
|
||||
halfblock: halfblock,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_rgb(rgb: Vec<u8>) -> u32 {
|
||||
let r = *rgb.get(0).unwrap() as u32;
|
||||
let g = *rgb.get(1).unwrap() as u32;
|
||||
let b = *rgb.get(2).unwrap() as u32;
|
||||
|
||||
let rgb = (r << 16) + (g << 8) + b;
|
||||
|
||||
return rgb
|
||||
}
|
||||
|
||||
pub fn halfblock_bitmap(bitmap: &Vec<Vec<u32>>) -> Vec<Vec<AnsiPixelPair>> {
|
||||
let ansi_bitmap = bitmap
|
||||
.iter()
|
||||
.map(|x| {
|
||||
x.iter().map(|y| AnsiPixel::new(y)).collect::<Vec<AnsiPixel>>()
|
||||
})
|
||||
.collect::<Vec<Vec<AnsiPixel>>>();
|
||||
|
||||
let mut ansi_canvas: Vec<Vec<AnsiPixelPair>> = Vec::new();
|
||||
|
||||
for two_rows in ansi_bitmap.chunks(2) {
|
||||
let rows = two_rows.to_vec();
|
||||
let top_row = rows.get(0).unwrap();
|
||||
let bottom_row = rows.get(1).unwrap();
|
||||
|
||||
let mut ansi_row: Vec<AnsiPixelPair> = Vec::new();
|
||||
|
||||
for i in 0..bitmap.get(0).unwrap().len() {
|
||||
let top_pixel = top_row.get(i as usize).unwrap();
|
||||
let bottom_pixel = bottom_row.get(i as usize).unwrap();
|
||||
|
||||
let pixel_pair = AnsiPixelPair {
|
||||
top: *top_pixel,
|
||||
bottom: *bottom_pixel,
|
||||
};
|
||||
|
||||
ansi_row.push(pixel_pair);
|
||||
}
|
||||
|
||||
ansi_canvas.push(ansi_row);
|
||||
}
|
||||
|
||||
ansi_canvas
|
||||
}
|
||||
|
||||
pub fn ansi_draw(image: AnsiImage) -> String {
|
||||
let mut out: String = String::new();
|
||||
for row in image.halfblock {
|
||||
for pixel_pair in row.iter() {
|
||||
let fg = pixel_pair.top.ansi;
|
||||
let bg = pixel_pair.bottom.ansi;
|
||||
out.push_str(format!("\x1b[38;5;{}m\x1b[48;5;{}m{}", fg, bg, CHAR).as_str());
|
||||
}
|
||||
out.push_str("\x1b[0m");
|
||||
out.push_str("\n");
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
pub fn irc_draw(image: AnsiImage) -> String {
|
||||
let mut out: String = String::new();
|
||||
for row in image.halfblock {
|
||||
let mut last_fg: u8 = 0;
|
||||
let mut last_bg: u8 = 0;
|
||||
for pixel_pair in row.iter() {
|
||||
let fg = pixel_pair.top.irc;
|
||||
let bg = pixel_pair.bottom.irc;
|
||||
|
||||
if fg == last_fg && bg == last_bg {
|
||||
out.push_str(format!("{}", CHAR).as_str());
|
||||
} else if bg == last_bg {
|
||||
out.push_str(format!("\x03{}{}", fg, CHAR).as_str());
|
||||
} else {
|
||||
out.push_str(format!("\x03{},{}{}", fg, bg, CHAR).as_str());
|
||||
}
|
||||
|
||||
last_fg = fg;
|
||||
last_bg = bg;
|
||||
}
|
||||
out.push_str("\n");
|
||||
}
|
||||
return out
|
||||
}
|
202
src/effects.rs
Normal file
202
src/effects.rs
Normal file
@ -0,0 +1,202 @@
|
||||
use crate::args;
|
||||
use photon_rs::{channels, channels::alter_channels, conv, effects, filters, monochrome, noise};
|
||||
use photon_rs::transform::{resize, SamplingFilter};
|
||||
use photon_rs::PhotonImage;
|
||||
|
||||
pub fn apply_effects(
|
||||
args: &args::Args,
|
||||
mut photon_image: PhotonImage,
|
||||
) -> PhotonImage {
|
||||
|
||||
// Resize to width
|
||||
let height =
|
||||
(args.width as f32 / photon_image.get_width() as f32 * photon_image.get_height() as f32) as u32;
|
||||
|
||||
photon_image = resize(&mut photon_image, args.width, height, SamplingFilter::Lanczos3);
|
||||
|
||||
// Adjust brightness
|
||||
if args.brightness != 0 {
|
||||
alter_channels(&mut photon_image, args.brightness, args.brightness, args.brightness);
|
||||
}
|
||||
|
||||
// Adjust hue
|
||||
if args.hue != 0 {
|
||||
alter_channels(&mut photon_image, args.hue, args.hue, args.hue);
|
||||
}
|
||||
|
||||
// Adjust contrast
|
||||
if args.contrast != 0 {
|
||||
alter_channels(&mut photon_image, args.contrast, args.contrast, args.contrast);
|
||||
}
|
||||
|
||||
// Adjust saturation
|
||||
if args.saturation != 0 {
|
||||
alter_channels(&mut photon_image, args.saturation, args.saturation, args.saturation);
|
||||
}
|
||||
|
||||
// Adjust opacity
|
||||
if args.opacity != 0 {
|
||||
alter_channels(&mut photon_image, args.opacity, args.opacity, args.opacity);
|
||||
}
|
||||
|
||||
// Adjust gamma
|
||||
if args.gamma != 0 {
|
||||
alter_channels(&mut photon_image, args.gamma, args.gamma, args.gamma);
|
||||
}
|
||||
|
||||
// Adjust dither
|
||||
if args.dither > 0 {
|
||||
effects::dither(&mut photon_image, args.dither);
|
||||
}
|
||||
|
||||
// Adjust gaussian_blur
|
||||
if args.gaussian_blur > 0 {
|
||||
|
||||
conv::gaussian_blur(&mut photon_image, args.gaussian_blur);
|
||||
}
|
||||
|
||||
// Adjust pixelize
|
||||
if args.pixelize > 0 {
|
||||
effects::pixelize(&mut photon_image, args.pixelize);
|
||||
}
|
||||
|
||||
// Adjust halftone
|
||||
if args.halftone {
|
||||
effects::halftone(&mut photon_image);
|
||||
}
|
||||
|
||||
// Adjust invert
|
||||
if args.invert {
|
||||
channels::invert(&mut photon_image);
|
||||
}
|
||||
|
||||
// Adjust sepia
|
||||
if args.sepia {
|
||||
monochrome::sepia(&mut photon_image);
|
||||
}
|
||||
|
||||
// Adjust solarize
|
||||
if args.solarize {
|
||||
effects::solarize(&mut photon_image);
|
||||
}
|
||||
|
||||
// Adjust normalize
|
||||
if args.normalize {
|
||||
effects::normalize(&mut photon_image);
|
||||
}
|
||||
|
||||
// Adjust noise
|
||||
if args.noise {
|
||||
noise::add_noise_rand(photon_image.clone());
|
||||
}
|
||||
|
||||
// Adjust sharpen
|
||||
if args.sharpen {
|
||||
conv::sharpen(&mut photon_image);
|
||||
}
|
||||
|
||||
// Adjust edge_detection
|
||||
if args.edge_detection {
|
||||
conv::edge_detection(&mut photon_image);
|
||||
}
|
||||
|
||||
// Adjust emboss
|
||||
if args.emboss {
|
||||
conv::emboss(&mut photon_image);
|
||||
}
|
||||
|
||||
// Adjust frosted_glass
|
||||
if args.frosted_glass {
|
||||
effects::frosted_glass(&mut photon_image);
|
||||
}
|
||||
|
||||
// Adjust box_blur
|
||||
if args.box_blur {
|
||||
conv::box_blur(&mut photon_image);
|
||||
}
|
||||
|
||||
// Adjust grayscale
|
||||
if args.grayscale {
|
||||
monochrome::grayscale(&mut photon_image);
|
||||
}
|
||||
|
||||
// Adjust identity
|
||||
if args.identity {
|
||||
conv::identity(&mut photon_image);
|
||||
}
|
||||
|
||||
// Adjust laplace
|
||||
if args.laplace {
|
||||
conv::laplace(&mut photon_image);
|
||||
}
|
||||
|
||||
// Adjust cali
|
||||
if args.cali {
|
||||
filters::cali(&mut photon_image);
|
||||
}
|
||||
|
||||
// Adjust dramatic
|
||||
if args.dramatic {
|
||||
filters::dramatic(&mut photon_image);
|
||||
}
|
||||
|
||||
// Adjust firenze
|
||||
if args.firenze {
|
||||
filters::firenze(&mut photon_image);
|
||||
}
|
||||
|
||||
// Adjust golden
|
||||
if args.golden {
|
||||
filters::golden(&mut photon_image);
|
||||
}
|
||||
|
||||
// Adjust lix
|
||||
if args.lix {
|
||||
filters::lix(&mut photon_image);
|
||||
}
|
||||
|
||||
// Adjust lofi
|
||||
if args.lofi {
|
||||
filters::lofi(&mut photon_image);
|
||||
}
|
||||
|
||||
// Adjust neue
|
||||
if args.neue {
|
||||
filters::neue(&mut photon_image);
|
||||
}
|
||||
|
||||
// Adjust obsidian
|
||||
if args.obsidian {
|
||||
filters::obsidian(&mut photon_image);
|
||||
}
|
||||
|
||||
// Adjust pastel_pink
|
||||
if args.pastel_pink {
|
||||
filters::pastel_pink(&mut photon_image);
|
||||
}
|
||||
|
||||
// Adjust ryo
|
||||
if args.ryo {
|
||||
filters::ryo(&mut photon_image);
|
||||
}
|
||||
|
||||
// Adjust oil
|
||||
match &args.oil {
|
||||
Some(oil) => {
|
||||
// split oil at comma
|
||||
let vals: Vec<&str> = oil.split(",").collect();
|
||||
|
||||
// check if args.oil has 2 values
|
||||
if vals.len() == 2 {
|
||||
// convert oil values to i32 and f64
|
||||
let radius: i32 = vals.get(0).unwrap().parse::<i32>().unwrap();
|
||||
let intensity: f64 = vals.get(1).unwrap().parse::<f64>().unwrap();
|
||||
|
||||
effects::oil(&mut photon_image, radius, intensity);
|
||||
}
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
|
||||
photon_image
|
||||
}
|
64
src/main.rs
Normal file
64
src/main.rs
Normal file
@ -0,0 +1,64 @@
|
||||
mod args;
|
||||
mod draw;
|
||||
mod palette;
|
||||
mod effects;
|
||||
|
||||
use reqwest;
|
||||
use url::Url;
|
||||
|
||||
use photon_rs::PhotonImage;
|
||||
use std::{error::Error, io::Cursor, process::exit};
|
||||
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let args = args::parse_args();
|
||||
|
||||
match load_image_from_url_or_path(args.image.as_str()).await {
|
||||
Ok(mut image) => {
|
||||
image = effects::apply_effects(
|
||||
&args,
|
||||
image,
|
||||
);
|
||||
|
||||
let canvas = draw::AnsiImage::new(image);
|
||||
match &args.render {
|
||||
None => println!("{}", draw::irc_draw(canvas).as_str()),
|
||||
Some(ref render) => match render.as_str() {
|
||||
"irc" => println!("{}", draw::irc_draw(canvas).as_str()),
|
||||
"ansi" => println!("{}", draw::ansi_draw(canvas).as_str()),
|
||||
_ => {
|
||||
eprintln!("Error: invalid render type");
|
||||
exit(1);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Error: {}", e);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn load_image_from_url_or_path(image: &str) -> Result<PhotonImage, Box<dyn Error>> {
|
||||
match Url::parse(image) {
|
||||
Ok(url) => {
|
||||
let response = reqwest::get(url).await?;
|
||||
let bytes = response.bytes().await?;
|
||||
|
||||
let image_data = Cursor::new(bytes);
|
||||
match photon_rs::native::open_image_from_bytes(image_data.into_inner().as_ref()) {
|
||||
Ok(image) => Ok(image),
|
||||
Err(e) => Err(Box::new(e)),
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
match photon_rs::native::open_image(image) {
|
||||
Ok(image) => Ok(image),
|
||||
Err(e) => Err(Box::new(e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
78
src/palette.rs
Normal file
78
src/palette.rs
Normal file
@ -0,0 +1,78 @@
|
||||
pub const RGB99: [u32; 99] = [
|
||||
0xffffff, 0x000000, 0x00007f, 0x009300, 0xff0000, 0x7f0000, 0x9c009c, 0xfc7f00,
|
||||
0xffff00, 0x00fc00, 0x009393, 0x00ffff, 0x0000fc, 0xff00ff, 0x7f7f7f, 0xd2d2d2,
|
||||
0x470000, 0x472100, 0x474700, 0x324700, 0x004700, 0x00472c, 0x004747, 0x002747,
|
||||
0x000047, 0x2e0047, 0x470047, 0x47002a, 0x740000, 0x743a00, 0x747400, 0x517400,
|
||||
0x007400, 0x007449, 0x007474, 0x004074, 0x000074, 0x4b0074, 0x740074, 0x740045,
|
||||
0xb50000, 0xb56300, 0xb5b500, 0x7db500, 0x00b500, 0x00b571, 0x00b5b5, 0x0063b5,
|
||||
0x0000b5, 0x7500b5, 0xb500b5, 0xb5006b, 0xff0000, 0xff8c00, 0xffff00, 0xb2ff00,
|
||||
0x00ff00, 0x00ffa0, 0x00ffff, 0x008cff, 0x0000ff, 0xa500ff, 0xff00ff, 0xff0098,
|
||||
0xff5959, 0xffb459, 0xffff71, 0xcfff60, 0x6fff6f, 0x65ffc9, 0x6dffff, 0x59b4ff,
|
||||
0x5959ff, 0xc459ff, 0xff66ff, 0xff59bc, 0xff9c9c, 0xffd39c, 0xffff9c, 0xe2ff9c,
|
||||
0x9cff9c, 0x9cffdb, 0x9cffff, 0x9cd3ff, 0x9c9cff, 0xdc9cff, 0xff9cff, 0xff94d3,
|
||||
0x000000, 0x131313, 0x282828, 0x363636, 0x4d4d4d, 0x656565, 0x818181, 0x9f9f9f,
|
||||
0xbcbcbc, 0xe2e2e2, 0xffffff,
|
||||
];
|
||||
|
||||
pub const ANSI256: [u32; 256] = [
|
||||
0x000000, 0x800000, 0x008000, 0x808000, 0x000080, 0x800080, 0x008080, 0xc0c0c0,
|
||||
0x808080, 0xff0000, 0x00ff00, 0xffff00, 0x0000ff, 0xff00ff, 0x00ffff, 0xffffff,
|
||||
0x000000, 0x00005f, 0x000087, 0x0000af, 0x0000d7, 0x0000ff, 0x005f00, 0x005f5f,
|
||||
0x005f87, 0x005faf, 0x005fd7, 0x005fff, 0x008700, 0x00875f, 0x008787, 0x0087af,
|
||||
0x0087d7, 0x0087ff, 0x00af00, 0x00af5f, 0x00af87, 0x00afaf, 0x00afd7, 0x00afff,
|
||||
0x00d700, 0x00d75f, 0x00d787, 0x00d7af, 0x00d7d7, 0x00d7ff, 0x00ff00, 0x00ff5f,
|
||||
0x00ff87, 0x00ffaf, 0x00ffd7, 0x00ffff, 0x5f0000, 0x5f005f, 0x5f0087, 0x5f00af,
|
||||
0x5f00d7, 0x5f00ff, 0x5f5f00, 0x5f5f5f, 0x5f5f87, 0x5f5faf, 0x5f5fd7, 0x5f5fff,
|
||||
0x5f8700, 0x5f875f, 0x5f8787, 0x5f87af, 0x5f87d7, 0x5f87ff, 0x5faf00, 0x5faf5f,
|
||||
0x5faf87, 0x5fafaf, 0x5fafd7, 0x5fafff, 0x5fd700, 0x5fd75f, 0x5fd787, 0x5fd7af,
|
||||
0x5fd7d7, 0x5fd7ff, 0x5fff00, 0x5fff5f, 0x5fff87, 0x5fffaf, 0x5fffd7, 0x5fffff,
|
||||
0x870000, 0x87005f, 0x870087, 0x8700af, 0x8700d7, 0x8700ff, 0x875f00, 0x875f5f,
|
||||
0x875f87, 0x875faf, 0x875fd7, 0x875fff, 0x878700, 0x87875f, 0x878787, 0x8787af,
|
||||
0x8787d7, 0x8787ff, 0x87af00, 0x87af5f, 0x87af87, 0x87afaf, 0x87afd7, 0x87afff,
|
||||
0x87d700, 0x87d75f, 0x87d787, 0x87d7af, 0x87d7d7, 0x87d7ff, 0x87ff00, 0x87ff5f,
|
||||
0x87ff87, 0x87ffaf, 0x87ffd7, 0x87ffff, 0xaf0000, 0xaf005f, 0xaf0087, 0xaf00af,
|
||||
0xaf00d7, 0xaf00ff, 0xaf5f00, 0xaf5f5f, 0xaf5f87, 0xaf5faf, 0xaf5fd7, 0xaf5fff,
|
||||
0xaf8700, 0xaf875f, 0xaf8787, 0xaf87af, 0xaf87d7, 0xaf87ff, 0xafaf00, 0xafaf5f,
|
||||
0xafaf87, 0xafafaf, 0xafafd7, 0xafafff, 0xafd700, 0xafd75f, 0xafd787, 0xafd7af,
|
||||
0xafd7d7, 0xafd7ff, 0xafff00, 0xafff5f, 0xafff87, 0xafffaf, 0xafffd7, 0xafffff,
|
||||
0xd70000, 0xd7005f, 0xd70087, 0xd700af, 0xd700d7, 0xd700ff, 0xd75f00, 0xd75f5f,
|
||||
0xd75f87, 0xd75faf, 0xd75fd7, 0xd75fff, 0xd78700, 0xd7875f, 0xd78787, 0xd787af,
|
||||
0xd787d7, 0xd787ff, 0xd7af00, 0xd7af5f, 0xd7af87, 0xd7afaf, 0xd7afd7, 0xd7afff,
|
||||
0xd7d700, 0xd7d75f, 0xd7d787, 0xd7d7af, 0xd7d7d7, 0xd7d7ff, 0xd7ff00, 0xd7ff5f,
|
||||
0xd7ff87, 0xd7ffaf, 0xd7ffd7, 0xd7ffff, 0xff0000, 0xff005f, 0xff0087, 0xff00af,
|
||||
0xff00d7, 0xff00ff, 0xff5f00, 0xff5f5f, 0xff5f87, 0xff5faf, 0xff5fd7, 0xff5fff,
|
||||
0xff8700, 0xff875f, 0xff8787, 0xff87af, 0xff87d7, 0xff87ff, 0xffaf00, 0xffaf5f,
|
||||
0xffaf87, 0xffafaf, 0xffafd7, 0xffafff, 0xffd700, 0xffd75f, 0xffd787, 0xffd7af,
|
||||
0xffd7d7, 0xffd7ff, 0xffff00, 0xffff5f, 0xffff87, 0xffffaf, 0xffffd7, 0xffffff,
|
||||
0x080808, 0x121212, 0x1c1c1c, 0x262626, 0x303030, 0x3a3a3a, 0x444444, 0x4e4e4e,
|
||||
0x585858, 0x626262, 0x6c6c6c, 0x767676, 0x808080, 0x8a8a8a, 0x949494, 0x9e9e9e,
|
||||
0xa8a8a8, 0xb2b2b2, 0xbcbcbc, 0xc6c6c6, 0xd0d0d0, 0xdadada, 0xe4e4e4, 0xeeeeee
|
||||
];
|
||||
|
||||
fn hex_to_rgb(hex: u32) -> (u8, u8, u8) {
|
||||
let r = ((hex >> 16) & 0xFF) as u8;
|
||||
let g = ((hex >> 8) & 0xFF) as u8;
|
||||
let b = (hex & 0xFF) as u8;
|
||||
|
||||
(r, g, b)
|
||||
}
|
||||
|
||||
fn color_distance_squared(c1: (u8, u8, u8), c2: (u8, u8, u8)) -> u32 {
|
||||
let dr = c1.0 as i32 - c2.0 as i32;
|
||||
let dg = c1.1 as i32 - c2.1 as i32;
|
||||
let db = c1.2 as i32 - c2.2 as i32;
|
||||
|
||||
(dr * dr + dg * dg + db * db) as u32
|
||||
}
|
||||
|
||||
pub fn nearest_hex_color(input_color: u32, hex_colors: Vec<u32>) -> u8 {
|
||||
let hex = hex_colors
|
||||
.iter()
|
||||
.map(|&hex| (hex, hex_to_rgb(hex)))
|
||||
.min_by_key(|(_, rgb)| color_distance_squared(hex_to_rgb(input_color), *rgb))
|
||||
.map(|(hex, _)| hex).unwrap();
|
||||
|
||||
let index = hex_colors.iter().position(|&x| x == hex).unwrap();
|
||||
return index as u8;
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user