brightness adjustment; find closest palette colour; tests
This commit is contained in:
parent
03588c4c55
commit
4bd896f05a
|
@ -6,6 +6,7 @@ import init, {
|
||||||
load_game,
|
load_game,
|
||||||
load_default_game,
|
load_default_game,
|
||||||
output,
|
output,
|
||||||
|
set_brightness,
|
||||||
set_dither,
|
set_dither,
|
||||||
set_room_name,
|
set_room_name,
|
||||||
} from './pkg/pixsy.js';
|
} from './pkg/pixsy.js';
|
||||||
|
@ -70,6 +71,7 @@ async function run() {
|
||||||
const buttonNewGame = el("new");
|
const buttonNewGame = el("new");
|
||||||
const buttonReset = el("reset");
|
const buttonReset = el("reset");
|
||||||
const checkboxDither = el("dither");
|
const checkboxDither = el("dither");
|
||||||
|
const inputBrightness = el("brightness");
|
||||||
const inputRoomName = el("room-name");
|
const inputRoomName = el("room-name");
|
||||||
const selectPalette = el("palette");
|
const selectPalette = el("palette");
|
||||||
const textareaGameDataInput = el("game-data");
|
const textareaGameDataInput = el("game-data");
|
||||||
|
@ -171,12 +173,18 @@ async function run() {
|
||||||
|
|
||||||
checkboxDither.addEventListener("change", () => {
|
checkboxDither.addEventListener("change", () => {
|
||||||
set_dither(checkboxDither.checked);
|
set_dither(checkboxDither.checked);
|
||||||
|
loadPreview();
|
||||||
});
|
});
|
||||||
|
|
||||||
inputRoomName.addEventListener("change", () => {
|
inputRoomName.addEventListener("change", () => {
|
||||||
set_room_name(inputRoomName.value);
|
set_room_name(inputRoomName.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
inputBrightness.addEventListener("input", () => {
|
||||||
|
set_brightness(inputBrightness.value);
|
||||||
|
loadPreview();
|
||||||
|
});
|
||||||
|
|
||||||
function addRoom() {
|
function addRoom() {
|
||||||
console.log(add_room());
|
console.log(add_room());
|
||||||
textareaGameDataOutput.value = output();
|
textareaGameDataOutput.value = output();
|
||||||
|
|
144
src/lib.rs
144
src/lib.rs
|
@ -1,3 +1,5 @@
|
||||||
|
#![feature(clamp)]
|
||||||
|
|
||||||
use bitsy_parser::game::Game;
|
use bitsy_parser::game::Game;
|
||||||
use bitsy_parser::image::Image;
|
use bitsy_parser::image::Image;
|
||||||
use bitsy_parser::tile::Tile;
|
use bitsy_parser::tile::Tile;
|
||||||
|
@ -10,21 +12,23 @@ use image::imageops::dither;
|
||||||
const SD: u32 = 8;
|
const SD: u32 = 8;
|
||||||
|
|
||||||
struct State {
|
struct State {
|
||||||
game: Option<Game>,
|
game: Option<Game>,
|
||||||
image: Option<DynamicImage>,
|
image: Option<DynamicImage>,
|
||||||
room_name: Option<String>,
|
room_name: Option<String>,
|
||||||
palette: Option<String>,
|
palette: Option<String>,
|
||||||
dither: bool,
|
dither: bool,
|
||||||
|
brightness: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref STATE: Mutex<State> = Mutex::new(
|
static ref STATE: Mutex<State> = Mutex::new(
|
||||||
State {
|
State {
|
||||||
game: None,
|
game: None,
|
||||||
image: None,
|
image: None,
|
||||||
room_name: None,
|
room_name: None,
|
||||||
palette: None,
|
palette: None,
|
||||||
dither: true,
|
dither: true,
|
||||||
|
brightness: 0,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -117,6 +121,12 @@ pub fn set_room_name(room_name: String) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn set_brightness(brightness: i32) {
|
||||||
|
let mut state = STATE.lock().unwrap();
|
||||||
|
state.brightness = brightness;
|
||||||
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub fn get_palettes() -> String {
|
pub fn get_palettes() -> String {
|
||||||
let state = STATE.lock().unwrap();
|
let state = STATE.lock().unwrap();
|
||||||
|
@ -146,15 +156,54 @@ fn image_to_base64(image: &DynamicImage) -> String {
|
||||||
format!("data:image/png;base64,{}", base64::encode(&bytes))
|
format!("data:image/png;base64,{}", base64::encode(&bytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_preview(image: &DynamicImage) -> DynamicImage {
|
fn colour_difference(compare: image::Rgba<u8>, other: &bitsy_parser::Colour) -> u32 {
|
||||||
let image = image.clone();
|
let diff_red = (compare[0] as i16 - other.red as i16).abs();
|
||||||
let image = image.grayscale();
|
let diff_green= (compare[1] as i16 - other.green as i16).abs();
|
||||||
|
let diff_blue = (compare[2] as i16 - other.blue as i16).abs();
|
||||||
|
|
||||||
|
(diff_red + diff_green + diff_blue) as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn closest_colour(compare: image::Rgba<u8>, colours: &[bitsy_parser::Colour]) -> image::Rgba<u8> {
|
||||||
|
let diff_background = colour_difference(compare, &colours[0]);
|
||||||
|
let diff_foreground = colour_difference(compare, &colours[1]);
|
||||||
|
|
||||||
|
if diff_foreground <= diff_background {
|
||||||
|
image::Rgba::from([colours[1].red, colours[1].green, colours[1].blue, 255])
|
||||||
|
} else {
|
||||||
|
image::Rgba::from([colours[0].red, colours[0].green, colours[0].blue, 255])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn adjust_brightness(pixel: &mut image::Rgba<u8>, state: &State) -> image::Rgba<u8> {
|
||||||
|
pixel[0] = (pixel[0] as i32 + state.brightness).clamp(0, 255) as u8;
|
||||||
|
pixel[1] = (pixel[1] as i32 + state.brightness).clamp(0, 255) as u8;
|
||||||
|
pixel[2] = (pixel[2] as i32 + state.brightness).clamp(0, 255) as u8;
|
||||||
|
|
||||||
|
*pixel
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_preview(state: &State) -> DynamicImage {
|
||||||
|
let image = state.image.as_ref().unwrap().clone();
|
||||||
|
let mut preview = image.clone();
|
||||||
|
|
||||||
// todo dither
|
// todo dither
|
||||||
|
|
||||||
// todo convert to palette colours
|
// todo convert to palette colours
|
||||||
|
|
||||||
image
|
// get background and foreground colours from palette
|
||||||
|
let colours = &state.game.as_ref().unwrap().palettes
|
||||||
|
.iter()
|
||||||
|
.find(|palette| &palette.id == state.palette.as_ref().unwrap())
|
||||||
|
.unwrap()
|
||||||
|
.colours[0..2];
|
||||||
|
|
||||||
|
for (x, y, mut pixel) in image.pixels() {
|
||||||
|
// is pixel closer to background or foreground?
|
||||||
|
preview.put_pixel(x, y, closest_colour(adjust_brightness(&mut pixel, &state), colours));
|
||||||
|
}
|
||||||
|
|
||||||
|
preview
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
|
@ -162,7 +211,7 @@ pub fn get_preview() -> String {
|
||||||
let state = STATE.lock().unwrap();
|
let state = STATE.lock().unwrap();
|
||||||
|
|
||||||
match &state.image.is_some() {
|
match &state.image.is_some() {
|
||||||
true => image_to_base64(&render_preview(state.image.as_ref().unwrap())),
|
true => image_to_base64(&render_preview(&state)),
|
||||||
false => "".to_string(),
|
false => "".to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -238,6 +287,7 @@ pub fn output() -> String {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::{add_room, load_image, load_default_game, output, get_preview};
|
use crate::{add_room, load_image, load_default_game, output, get_preview};
|
||||||
|
use image::Rgba;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn image_to_base64() {
|
fn image_to_base64() {
|
||||||
|
@ -247,6 +297,70 @@ mod test {
|
||||||
assert_eq!(output, expected);
|
assert_eq!(output, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn colour_difference_none() {
|
||||||
|
let output = crate::colour_difference(
|
||||||
|
Rgba::from([255; 4]),
|
||||||
|
&bitsy_parser::Colour {
|
||||||
|
red: 255,
|
||||||
|
green: 255,
|
||||||
|
blue: 255
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(output, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn colour_difference_some() {
|
||||||
|
let output = crate::colour_difference(
|
||||||
|
Rgba::from([255; 4]),
|
||||||
|
&bitsy_parser::Colour {
|
||||||
|
red: 254,
|
||||||
|
green: 255,
|
||||||
|
blue: 255
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(output, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn colour_difference_some_2() {
|
||||||
|
let output = crate::colour_difference(
|
||||||
|
Rgba::from([254; 4]),
|
||||||
|
&bitsy_parser::Colour {
|
||||||
|
red: 254,
|
||||||
|
green: 255,
|
||||||
|
blue: 254
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(output, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn colour_difference_max() {
|
||||||
|
let expected = 255 * 3;
|
||||||
|
|
||||||
|
let output = crate::colour_difference(
|
||||||
|
Rgba::from([0; 4]),
|
||||||
|
&bitsy_parser::Colour {
|
||||||
|
red: 255,
|
||||||
|
green: 255,
|
||||||
|
blue: 255
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(output, expected);
|
||||||
|
|
||||||
|
let output = crate::colour_difference(
|
||||||
|
Rgba::from([255; 4]),
|
||||||
|
&bitsy_parser::Colour {
|
||||||
|
red: 0,
|
||||||
|
green: 0,
|
||||||
|
blue: 0
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(output, expected);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn get_palettes() {
|
fn get_palettes() {
|
||||||
load_default_game();
|
load_default_game();
|
||||||
|
|
Loading…
Reference in New Issue