use oklch instead of hsv for hue shifts

This commit is contained in:
bgkillas 2024-10-30 09:39:23 -04:00
parent e4ea0213ef
commit d235795efd
3 changed files with 100 additions and 58 deletions

25
noita-proxy/Cargo.lock generated
View file

@ -1271,9 +1271,9 @@ dependencies = [
[[package]] [[package]]
name = "glam" name = "glam"
version = "0.29.0" version = "0.29.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c28091a37a5d09b555cb6628fd954da299b536433834f5b8e59eba78e0cbbf8a" checksum = "480c9417a5dc586fc0c0cb67891170e59cc11e9dc79ba1c11ddd2c56ca3f3b90"
[[package]] [[package]]
name = "globset" name = "globset"
@ -1507,9 +1507,9 @@ dependencies = [
[[package]] [[package]]
name = "hyper-util" name = "hyper-util"
version = "0.1.9" version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures-channel", "futures-channel",
@ -2530,10 +2530,11 @@ dependencies = [
[[package]] [[package]]
name = "quinn-udp" name = "quinn-udp"
version = "0.5.5" version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fe68c2e9e1a1234e218683dbdf9f9dfcb094113c5ac2b938dfcb9bab4c4140b" checksum = "e346e016eacfff12233c243718197ca12f148c84e1e84268a896699b41c71780"
dependencies = [ dependencies = [
"cfg_aliases",
"libc", "libc",
"once_cell", "once_cell",
"socket2", "socket2",
@ -2680,9 +2681,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]] [[package]]
name = "reqwest" name = "reqwest"
version = "0.12.8" version = "0.12.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f713147fbe92361e52392c73b8c9e48c04c6625bce969ef54dc901e58e042a7b" checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f"
dependencies = [ dependencies = [
"base64 0.22.1", "base64 0.22.1",
"bytes", "bytes",
@ -2992,18 +2993,18 @@ checksum = "d369a96f978623eb3dc28807c4852d6cc617fed53da5d3c400feff1ef34a714a"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.213" version = "1.0.214"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ea7893ff5e2466df8d720bb615088341b295f849602c6956047f8f80f0e9bc1" checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.213" version = "1.0.214"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e85ad2009c50b58e87caa8cd6dac16bdf511bbfb7af6c33df902396aa480fa5" checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",

View file

@ -156,7 +156,7 @@ enum AppState {
struct PlayerAppearance { struct PlayerAppearance {
player_color: PlayerColor, player_color: PlayerColor,
player_picker: PlayerPicker, player_picker: PlayerPicker,
hue: f32, hue: f64,
cosmetics: (bool, bool, bool), cosmetics: (bool, bool, bool),
} }
@ -204,23 +204,23 @@ impl Default for AppSavedState {
#[derive(Debug, Serialize, Deserialize, Decode, Encode, Copy, Clone)] #[derive(Debug, Serialize, Deserialize, Decode, Encode, Copy, Clone)]
pub struct PlayerColor { pub struct PlayerColor {
player_main: [u8; 4], player_main: [f64; 4],
player_alt: [u8; 4], player_alt: [f64; 4],
player_arm: [u8; 4], player_arm: [f64; 4],
player_cape: [u8; 4], player_cape: [f64; 4],
player_cape_edge: [u8; 4], player_cape_edge: [f64; 4],
player_forearm: [u8; 4], player_forearm: [f64; 4],
} }
impl Default for PlayerColor { impl Default for PlayerColor {
fn default() -> Self { fn default() -> Self {
Self { Self {
player_main: [155, 111, 154, 255], player_main: [155.0, 111.0, 154.0, 255.0],
player_alt: [127, 84, 118, 255], player_alt: [127.0, 84.0, 118.0, 255.0],
player_arm: [89, 67, 84, 255], player_arm: [89.0, 67.0, 84.0, 255.0],
player_cape: [118, 84, 127, 255], player_cape: [118.0, 84.0, 127.0, 255.0],
player_cape_edge: [154, 111, 155, 255], player_cape_edge: [154.0, 111.0, 155.0, 255.0],
player_forearm: [158, 115, 154, 255], player_forearm: [158.0, 115.0, 154.0, 255.0],
} }
} }
} }

View file

@ -5,7 +5,6 @@ use bitcode::{Decode, Encode};
use eframe::egui; use eframe::egui;
use eframe::egui::color_picker::{color_picker_color32, Alpha}; use eframe::egui::color_picker::{color_picker_color32, Alpha};
use eframe::egui::{Color32, TextureHandle, TextureOptions, Ui}; use eframe::egui::{Color32, TextureHandle, TextureOptions, Ui};
use eframe::epaint::Hsva;
use image::{Rgba, RgbaImage}; use image::{Rgba, RgbaImage};
use std::ffi::OsString; use std::ffi::OsString;
use std::fs::{self, File}; use std::fs::{self, File};
@ -51,19 +50,23 @@ pub fn replace_color(image: &mut RgbaImage, main: Rgba<u8>, alt: Rgba<u8>, arm:
} }
} }
fn to_u8(c: [f64; 4]) -> [u8; 4] {
[c[0] as u8, c[1] as u8, c[2] as u8, c[3] as u8]
}
pub fn make_player_image(image: &mut RgbaImage, colors: PlayerColor) { pub fn make_player_image(image: &mut RgbaImage, colors: PlayerColor) {
let target_main = Rgba::from([155, 111, 154, 255]); let target_main = Rgba::from([155, 111, 154, 255]);
let target_alt = Rgba::from([127, 84, 118, 255]); let target_alt = Rgba::from([127, 84, 118, 255]);
let target_arm = Rgba::from([89, 67, 84, 255]); let target_arm = Rgba::from([89, 67, 84, 255]);
let main = Rgba::from(colors.player_main); let main = Rgba::from(to_u8(colors.player_main));
let alt = Rgba::from(colors.player_alt); let alt = Rgba::from(to_u8(colors.player_alt));
let arm = Rgba::from(colors.player_arm); let arm = Rgba::from(to_u8(colors.player_arm));
let cape = Rgba::from(colors.player_cape); let cape = Rgba::from(to_u8(colors.player_cape));
let cape_edge = Rgba::from(colors.player_cape_edge); let cape_edge = Rgba::from(to_u8(colors.player_cape_edge));
let forearm = Rgba::from(colors.player_forearm); let forearm = Rgba::from(to_u8(colors.player_forearm));
for (i, pixel) in image.pixels_mut().enumerate() { for (i, pixel) in image.pixels_mut().enumerate() {
if *pixel == target_main { if *pixel == target_main {
*pixel = main; *pixel = main
} else if *pixel == target_alt { } else if *pixel == target_alt {
*pixel = alt *pixel = alt
} else if *pixel == target_arm { } else if *pixel == target_arm {
@ -112,13 +115,51 @@ pub fn add_cosmetics(
} }
} }
pub fn shift_hue(diff: f32, color: &mut [u8; 4]) { fn rgb_to_oklch(color: &mut [f64; 4]) {
let rgb = Color32::from_rgb(color[0], color[1], color[2]); let mut l = 0.4122214708 * color[0] + 0.5363325363 * color[1] + 0.0514459929 * color[2];
let mut hsv = Hsva::from(rgb); let mut m = 0.2119034982 * color[0] + 0.6806995451 * color[1] + 0.1073969566 * color[2];
hsv.h += diff / 360.0; let mut s = 0.0883024619 * color[0] + 0.2817188376 * color[1] + 0.6299787005 * color[2];
hsv.h = hsv.h.fract();
let rgb = hsv.to_srgb(); l = l.cbrt();
*color = [rgb[0], rgb[1], rgb[2], 255]; m = m.cbrt();
s = s.cbrt();
color[0] = 0.2104542553 * l + 0.7936177850 * m - 0.0040720468 * s;
color[1] = 1.9779984951 * l - 2.4285922050 * m + 0.4505937099 * s;
color[2] = 0.0259040371 * l + 0.7827717662 * m - 0.8086757660 * s;
}
fn oklch_to_rgb(color: &mut [f64; 4]) {
let mut l = color[0] + 0.3963377774 * color[1] + 0.2158037573 * color[2];
let mut m = color[0] - 0.1055613458 * color[1] - 0.0638541728 * color[2];
let mut s = color[0] - 0.0894841775 * color[1] - 1.2914855480 * color[2];
l = l.powi(3);
m = m.powi(3);
s = s.powi(3);
color[0] = 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s;
color[1] = -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s;
color[2] = -0.0041960863 * l - 0.7034186147 * m + 1.7076147010 * s;
}
fn shift_hue_by(color: &mut [f64; 4], diff: f64) {
let tau = std::f64::consts::TAU;
let diff = tau * diff / 360.0;
let c = (color[1].powi(2) + color[2].powi(2)).sqrt();
let hue = color[2].atan2(color[1]);
let mut new_hue = (hue + diff) % tau;
if new_hue.is_sign_negative() {
new_hue += tau;
}
color[1] = c * new_hue.cos();
color[2] = c * new_hue.sin();
}
pub fn shift_hue(diff: f64, color: &mut [f64; 4]) {
rgb_to_oklch(color);
shift_hue_by(color, diff);
oklch_to_rgb(color);
} }
pub fn player_skin_display_color_picker( pub fn player_skin_display_color_picker(
@ -149,10 +190,10 @@ pub fn player_skin_display_color_picker(
} }
} }
pub fn color_picker(ui: &mut Ui, color: &mut [u8; 4]) { pub fn color_picker(ui: &mut Ui, color: &mut [f64; 4]) {
let mut rgb = Color32::from_rgb(color[0], color[1], color[2]); let mut rgb = Color32::from_rgb(color[0] as u8, color[1] as u8, color[2] as u8);
color_picker_color32(ui, &mut rgb, Alpha::Opaque); color_picker_color32(ui, &mut rgb, Alpha::Opaque);
*color = [rgb.r(), rgb.g(), rgb.b(), 255] *color = [rgb.r() as f64, rgb.g() as f64, rgb.b() as f64, 255.0]
} }
pub fn player_select_current_color_slot(ui: &mut Ui, app: &mut App) { pub fn player_select_current_color_slot(ui: &mut Ui, app: &mut App) {
@ -258,30 +299,30 @@ pub fn create_player_png(
let mut img = image::open(player_path).unwrap().into_rgba8(); let mut img = image::open(player_path).unwrap().into_rgba8();
replace_color( replace_color(
&mut img, &mut img,
Rgba::from(rgb.player_main), Rgba::from(to_u8(rgb.player_main)),
Rgba::from(rgb.player_alt), Rgba::from(to_u8(rgb.player_alt)),
Rgba::from(rgb.player_arm), Rgba::from(to_u8(rgb.player_arm)),
); );
let mut img_arrow = image::open(arrows_path).unwrap().into_rgba8(); let mut img_arrow = image::open(arrows_path).unwrap().into_rgba8();
replace_color( replace_color(
&mut img_arrow, &mut img_arrow,
Rgba::from(rgb.player_main), Rgba::from(to_u8(rgb.player_main)),
Rgba::from(rgb.player_alt), Rgba::from(to_u8(rgb.player_alt)),
Rgba::from(rgb.player_arm), Rgba::from(to_u8(rgb.player_arm)),
); );
let mut img_ping = image::open(ping_path).unwrap().into_rgba8(); let mut img_ping = image::open(ping_path).unwrap().into_rgba8();
replace_color( replace_color(
&mut img_ping, &mut img_ping,
Rgba::from(rgb.player_main), Rgba::from(to_u8(rgb.player_main)),
Rgba::from(rgb.player_alt), Rgba::from(to_u8(rgb.player_alt)),
Rgba::from(rgb.player_arm), Rgba::from(to_u8(rgb.player_arm)),
); );
let mut img_cursor = image::open(cursor_path).unwrap().into_rgba8(); let mut img_cursor = image::open(cursor_path).unwrap().into_rgba8();
replace_color( replace_color(
&mut img_cursor, &mut img_cursor,
Rgba::from(rgb.player_main), Rgba::from(to_u8(rgb.player_main)),
Rgba::from(rgb.player_alt), Rgba::from(to_u8(rgb.player_alt)),
Rgba::from(rgb.player_arm), Rgba::from(to_u8(rgb.player_arm)),
); );
let path = tmp_path.join(format!("tmp/{}.png", id)); let path = tmp_path.join(format!("tmp/{}.png", id));
img.save(path).unwrap(); img.save(path).unwrap();
@ -291,7 +332,7 @@ pub fn create_player_png(
img_ping.save(path).unwrap(); img_ping.save(path).unwrap();
let path = tmp_path.join(format!("tmp/{}_cursor.png", id)); let path = tmp_path.join(format!("tmp/{}_cursor.png", id));
img_cursor.save(path).unwrap(); img_cursor.save(path).unwrap();
let img = create_arm(Rgba::from(rgb.player_forearm)); let img = create_arm(Rgba::from(to_u8(rgb.player_forearm)));
let path = tmp_path.join(format!("tmp/{}_arm.png", id)); let path = tmp_path.join(format!("tmp/{}_arm.png", id));
img.save(path).unwrap(); img.save(path).unwrap();
edit_nth_line( edit_nth_line(
@ -301,10 +342,10 @@ pub fn create_player_png(
.into_os_string(), .into_os_string(),
vec![16, 16], vec![16, 16],
vec![ vec![
format!("cloth_color=\"0xFF{}\"", rgb_to_hex(rgb.player_cape)), format!("cloth_color=\"0xFF{}\"", rgb_to_hex(to_u8(rgb.player_cape))),
format!( format!(
"cloth_color_edge=\"0xFF{}\"", "cloth_color_edge=\"0xFF{}\"",
rgb_to_hex(rgb.player_cape_edge) rgb_to_hex(to_u8(rgb.player_cape_edge))
), ),
], ],
); );