Compare commits
3 Commits
31d7ff52ca
...
6a58e8c003
Author | SHA1 | Date |
---|---|---|
Max Bradbury | 6a58e8c003 | |
Max Bradbury | aea19fcd6c | |
Max Bradbury | db3cdf42ff |
12
Cargo.toml
12
Cargo.toml
|
@ -11,9 +11,9 @@ repository = "https://tinybird.dev/max/image-to-bitsy"
|
|||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
"base64" = "^0.12.3"
|
||||
"bitsy-parser" = "^0.72.4"
|
||||
"image" = "^0.23.7"
|
||||
"json" = "^0.12.4"
|
||||
"lazy_static" = "^1.4.0"
|
||||
"wasm-bindgen" = "=0.2.64" # newer versions are bugged...
|
||||
base64 = "^0.12.3"
|
||||
bitsy-parser = "^0.72.5"
|
||||
image = "^0.23.7"
|
||||
json = "^0.12.4"
|
||||
lazy_static = "^1.4.0"
|
||||
wasm-bindgen = "=0.2.64" # newer versions are bugged...
|
||||
|
|
|
@ -62,6 +62,12 @@ html(lang="en-gb")
|
|||
| palette
|
||||
select#palette
|
||||
|
||||
#new-palette(style="display: none;")
|
||||
.half
|
||||
input#colour-background(type="color" value="#2f4ac9")
|
||||
.half
|
||||
input#colour-foreground(type="color" value="#8798fe")
|
||||
|
||||
label
|
||||
input#dither(type="checkbox" checked=true)
|
||||
| dither
|
||||
|
|
25
script.js
25
script.js
|
@ -72,7 +72,10 @@ async function run() {
|
|||
const buttonNewGame = el("new");
|
||||
const buttonReset = el("reset");
|
||||
const checkboxDither = el("dither");
|
||||
const divNewPalette = el("new-palette");
|
||||
const inputBrightness = el("brightness");
|
||||
const inputColourBackground = el("colour-background");
|
||||
const inputColourForeground = el("colour-foreground");
|
||||
const inputRoomName = el("room-name");
|
||||
const selectPalette = el("palette");
|
||||
const textareaGameDataInput = el("game-data");
|
||||
|
@ -126,6 +129,11 @@ async function run() {
|
|||
|
||||
selectPalette.innerHTML = "";
|
||||
|
||||
palettes.push({
|
||||
id: "NEW_PALETTE",
|
||||
name: "new palette"
|
||||
});
|
||||
|
||||
for (let palette of palettes) {
|
||||
let option = document.createElement("option");
|
||||
|
||||
|
@ -174,10 +182,25 @@ async function run() {
|
|||
buttonImageProceed.addEventListener("touchend", handleImage);
|
||||
|
||||
selectPalette.addEventListener("change", () => {
|
||||
set_palette(selectPalette.value);
|
||||
set_palette(selectPalette.value, inputColourBackground.value, inputColourForeground.value);
|
||||
|
||||
if (selectPalette.value === "NEW_PALETTE") {
|
||||
divNewPalette.style.display = "block";
|
||||
} else {
|
||||
divNewPalette.style.display = "none";
|
||||
}
|
||||
|
||||
loadPreview();
|
||||
});
|
||||
|
||||
function updateCustomPalette() {
|
||||
set_palette(selectPalette.value, inputColourBackground.value, inputColourForeground.value);
|
||||
loadPreview();
|
||||
}
|
||||
|
||||
inputColourForeground.addEventListener("change", updateCustomPalette);
|
||||
inputColourBackground.addEventListener("change", updateCustomPalette);
|
||||
|
||||
checkboxDither.addEventListener("change", () => {
|
||||
set_dither(checkboxDither.checked);
|
||||
loadPreview();
|
||||
|
|
67
src/lib.rs
67
src/lib.rs
|
@ -16,11 +16,22 @@ use colour_map::ColourMap;
|
|||
|
||||
const SD: u32 = 8;
|
||||
|
||||
enum SelectedPalette {
|
||||
None,
|
||||
Existing {
|
||||
id: String,
|
||||
},
|
||||
New {
|
||||
background: bitsy_parser::Colour,
|
||||
foreground: bitsy_parser::Colour,
|
||||
}
|
||||
}
|
||||
|
||||
struct State {
|
||||
game: Option<Game>,
|
||||
image: Option<DynamicImage>,
|
||||
room_name: Option<String>,
|
||||
palette: Option<String>,
|
||||
palette: SelectedPalette,
|
||||
dither: bool,
|
||||
brightness: i32,
|
||||
}
|
||||
|
@ -31,7 +42,7 @@ lazy_static! {
|
|||
game: None,
|
||||
image: None,
|
||||
room_name: None,
|
||||
palette: None,
|
||||
palette: SelectedPalette::None,
|
||||
dither: true,
|
||||
brightness: 0,
|
||||
}
|
||||
|
@ -49,8 +60,13 @@ fn tile_name(prefix: &Option<String>, index: &u32) -> Option<String> {
|
|||
#[wasm_bindgen]
|
||||
pub fn load_default_game() {
|
||||
let mut state = STATE.lock().unwrap();
|
||||
|
||||
state.game = Some(bitsy_parser::mock::game_default());
|
||||
state.palette = Some(bitsy_parser::mock::game_default().palette_ids()[0].clone())
|
||||
|
||||
// yes, this will probably always just be "0", but to be safe…
|
||||
state.palette = SelectedPalette::Existing {
|
||||
id: bitsy_parser::mock::game_default().palette_ids()[0].clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
|
@ -63,12 +79,12 @@ pub fn load_game(game_data: String) -> String {
|
|||
Ok((game, _errs)) => {
|
||||
let palette_id = game.palette_ids()[0].clone();
|
||||
state.game = Some(game);
|
||||
state.palette = Some(palette_id);
|
||||
state.palette = SelectedPalette::Existing { id: palette_id };
|
||||
format!("Loaded game")
|
||||
},
|
||||
_ => {
|
||||
state.game = None;
|
||||
state.palette = None;
|
||||
state.palette = SelectedPalette::None;
|
||||
format!("{}", result.err().unwrap())
|
||||
}
|
||||
}
|
||||
|
@ -110,12 +126,16 @@ pub fn set_dither(dither: bool) {
|
|||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn set_palette(palette_id: String) {
|
||||
pub fn set_palette(palette_id: &str, background: String, foreground: String) {
|
||||
let mut state = STATE.lock().unwrap();
|
||||
|
||||
match palette_id.is_empty() {
|
||||
true => { state.palette = None },
|
||||
false => { state.palette = Some(palette_id) },
|
||||
state.palette = match palette_id {
|
||||
"NEW_PALETTE" => SelectedPalette::New {
|
||||
background: bitsy_parser::Colour::from_hex(&background).unwrap(),
|
||||
foreground: bitsy_parser::Colour::from_hex(&foreground).unwrap(),
|
||||
},
|
||||
"" => SelectedPalette::None,
|
||||
_ => SelectedPalette::Existing { id: palette_id.to_string() },
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -166,10 +186,22 @@ fn image_to_base64(image: &DynamicImage) -> String {
|
|||
|
||||
fn render_preview(state: &State) -> DynamicImage {
|
||||
let mut buffer = state.image.as_ref().unwrap().clone().into_rgba();
|
||||
let palette_id = state.palette.as_ref().unwrap();
|
||||
let palette = *&state.game.as_ref().unwrap().get_palette(palette_id).unwrap();
|
||||
let colour_map = crate::ColourMap::from(palette);
|
||||
|
||||
let palette = match &state.palette {
|
||||
SelectedPalette::None => bitsy_parser::mock::game_default().palettes[0].clone(),
|
||||
SelectedPalette::Existing { id } => state.game.as_ref().unwrap().get_palette(id).unwrap().clone(),
|
||||
SelectedPalette::New { background, foreground } => Palette {
|
||||
id: "0".to_string(),
|
||||
name: None,
|
||||
colours: vec![
|
||||
background.clone(), foreground.clone(), Colour { red: 0, green: 0, blue: 0 }
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
let colour_map = crate::ColourMap::from(&palette);
|
||||
|
||||
// adjust brightness
|
||||
let mut buffer = image::imageops::brighten(&mut buffer, state.brightness);
|
||||
|
||||
if state.dither {
|
||||
|
@ -186,17 +218,6 @@ fn render_preview(state: &State) -> DynamicImage {
|
|||
.expect("indexed color out-of-range")
|
||||
.into()
|
||||
});
|
||||
let mut indexed = buffer.clone();
|
||||
for (i, pixel) in buffer.pixels().enumerate() {
|
||||
// todo get rid of magic numbers! what about Bitsy HD?
|
||||
let mut pixel = image::Rgba::from(pixel.0);
|
||||
colour_map.map_color(&mut pixel);
|
||||
indexed.put_pixel(
|
||||
(i % 128) as u32,
|
||||
(i / 128) as u32,
|
||||
pixel
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
image::DynamicImage::ImageRgba8(buffer)
|
||||
|
|
Loading…
Reference in New Issue