diff --git a/Cargo.toml b/Cargo.toml index 3738210..2a5c73c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ crate-type = ["cdylib"] [dependencies] base64 = "^0.12.3" bitsy-parser = "^0.72.5" +dither = "^1.3.9" image = "^0.23.7" json = "^0.12.4" lazy_static = "^1.4.0" diff --git a/index.pug b/index.pug index 27f4c8d..f8b893b 100644 --- a/index.pug +++ b/index.pug @@ -74,9 +74,12 @@ html(lang="en-gb") input#colour-foreground(type="color" value="#ffffff") label - input#dither(type="checkbox" checked=true) | dither - br + select#dither + option None + option(value="Atkinson" selected=true) Atkinson + option(value="Bayer4x4") Bayer 4×4 + option(value="FloydSteinberg") Floyd-Steinberg button.pagination.prev#back-to-image previous button.pagination.next#room-next add room diff --git a/src/lib.rs b/src/lib.rs index b6178a1..5669d2f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,7 @@ use bitsy_parser::game::Game; use bitsy_parser::image::Image; use bitsy_parser::tile::Tile; +use dither::prelude::Dither; use image::{GenericImageView, Pixel, DynamicImage}; use image::imageops::ColorMap; use image::imageops::FilterType::CatmullRom; @@ -27,12 +28,18 @@ enum SelectedPalette { } } +enum DitherAlgorithm { + Atkinson, + Bayer4x4, + FloydSteinberg, +} + struct State { game: Option, image: Option, room_name: Option, palette: SelectedPalette, - dither: bool, + dither: Option, brightness: i32, } @@ -43,7 +50,7 @@ lazy_static! { image: None, room_name: None, palette: SelectedPalette::None, - dither: true, + dither: Some(DitherAlgorithm::Atkinson), brightness: 0, } ); @@ -126,9 +133,15 @@ pub fn load_image(image_base64: String) -> String { } #[wasm_bindgen] -pub fn set_dither(dither: bool) { +pub fn set_dither(dither: &str) { let mut state = STATE.lock().unwrap(); - state.dither = dither; + + state.dither = match dither { + "Atkinson" => Some(DitherAlgorithm::Atkinson), + "Bayer4x4" => Some(DitherAlgorithm::Bayer4x4), + "FloydSteinberg" => Some(DitherAlgorithm::FloydSteinberg), + _ => None, + } } #[wasm_bindgen] @@ -204,25 +217,55 @@ fn render_preview(state: &State) -> DynamicImage { let mut buffer = state.image.as_ref().unwrap().clone().into_rgba(); 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_from(background, foreground), + 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_from(background, foreground) + }, }; let colour_map = crate::ColourMap::from(&palette); // adjust brightness - let mut buffer = image::imageops::brighten(&mut buffer, state.brightness); + let mut buffer = image::imageops::brighten( + &mut buffer, + state.brightness + ); - if state.dither { - image::imageops::dither(&mut buffer, &colour_map); + if state.dither.is_some() { + // image::imageops::dither(&mut buffer, &colour_map); + + // so, what needs doing? + // convert the buffer to the format required by the dither crate + + let dither_img = dither::prelude::Img::new(buffer.iter(), 128).unwrap(); + + // run the dither according to user preference + + // todo what is the quantise function supposed to be like? + // dither::ditherer::ATKINSON.dither(dither_img, colour_map); + + // convert the dither format back to an ImageBuffer + // buffer = image::ImageBuffer::new(128, 128); //from_vec(128, 128, pixels).unwrap(); + // + // for pixel in dither_img.iter() { + // // todo enumerate? + // buffer.put_pixel(0,0, image::Rgba::from([0,0,0,0])); + // } } else { // just do colour indexing - let indices = image::imageops::colorops::index_colors(&mut buffer, &colour_map); + let colour_indices = image::imageops::colorops::index_colors( + &mut buffer, + &colour_map + ); // todo get rid of magic numbers! what about Bitsy HD? buffer = image::ImageBuffer::from_fn(128, 128, |x, y| -> image::Rgba { - let p = indices.get_pixel(x, y); + let p = colour_indices.get_pixel(x, y); colour_map .lookup(p.0[0] as usize)