diff --git a/.gitignore b/.gitignore
index 55d94da..249bc38 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,3 @@
/target
*.lock
-bmp/out.bmp
+bmp/out.*
diff --git a/Cargo.toml b/Cargo.toml
index 2fb99f0..5f867f1 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -9,4 +9,16 @@ package.repository = "https://codeberg.org/p6nj/bent"
package.keywords = ["databending", "data-bending", "bending", "bend", "art"]
[workspace.dependencies]
+fundsp = "0.18.2"
+image = "0.25.1"
+anyhow = "1.0.86"
+bingus = { version = "0.1.0", path = "bingus" }
+num-traits = "0.2.19"
dasp_sample = "0.11.0"
+rayon = "1.10.0"
+dirs = "5.0.1"
+rfd = "0.14.1"
+derive-new = "0.6.0"
+infer = "0.16.0"
+indicatif = { version = "0.17.8", features = ["rayon"] }
+getset = "0.1.2"
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..68a49da
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,24 @@
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to
diff --git a/bent-funny-zone/Cargo.toml b/bent-funny-zone/Cargo.toml
index aec3e0f..1d7e5a2 100644
--- a/bent-funny-zone/Cargo.toml
+++ b/bent-funny-zone/Cargo.toml
@@ -11,7 +11,14 @@ keywords.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
-anyhow = "1.0.86"
-bmp = "0.5.0"
-dasp_sample = "0.11.0"
-fundsp = { version = "0.18.1", default-features = false, features = ["std"] }
+image = { workspace = true }
+fundsp = { workspace = true }
+anyhow = { workspace = true }
+bingus = { workspace = true }
+dasp_sample = { workspace = true }
+rayon = { workspace = true }
+dirs = { workspace = true }
+rfd = { workspace = true }
+derive-new = { workspace = true }
+infer = { workspace = true }
+indicatif = { workspace = true }
diff --git a/bent-funny-zone/src/main.rs b/bent-funny-zone/src/main.rs
index a4f4ea6..22e1e15 100644
--- a/bent-funny-zone/src/main.rs
+++ b/bent-funny-zone/src/main.rs
@@ -1,75 +1,131 @@
-use std::fs::File;
+use std::path::PathBuf;
-use anyhow::Result;
-use bmp::{open, Pixel};
-use dasp_sample::{FromSample, Sample, ToSample, U24};
+use anyhow::{Context, Result};
+use bingus::rawdata::RawData;
+use dasp_sample::{Sample, U24};
+use derive_new::new;
+use dirs::{download_dir, home_dir, picture_dir};
+use fundsp::math::uparc;
+use image::{io::Reader as ImageReader, ImageFormat, RgbImage};
+use indicatif::{ParallelProgressIterator, ProgressStyle};
+use rayon::{iter::ParallelIterator, slice::ParallelSlice};
+use rfd::FileDialog;
+
+struct Files {
+ input: PathBuf,
+ output: PathBuf,
+}
+
+#[derive(new)]
+struct DataPath {
+ files: Files,
+}
+
+impl Files {
+ fn try_new(input: PathBuf, output: PathBuf) -> Result {
+ Ok(Files {
+ input: Self::verify_image_file(input, "input")?,
+ output: Self::verify_image_file(output, "output")?,
+ })
+ }
+ fn verify_image_file(path: PathBuf, label: &'static str) -> Result {
+ ImageFormat::from_path(&path).with_context(|| {
+ format!("your {label} image must have a supported extension from this list:")
+ + &ImageFormat::all()
+ .map(|format| {
+ format!(
+ "\n\t{:?} ({})",
+ format,
+ format
+ .extensions_str()
+ .iter()
+ .fold(String::new(), |acc, x| acc + &format!(".{x}, "))
+ .strip_suffix(", ")
+ .unwrap()
+ )
+ })
+ .reduce(|acc, x| acc + &x)
+ .unwrap()
+ })?;
+ Ok(path)
+ }
+ fn prompt() -> Result {
+ Files::try_new(
+ FileDialog::new()
+ .set_title("Input image")
+ .set_directory(working_dir())
+ .add_filter(
+ "images",
+ &ImageFormat::all()
+ .map(ImageFormat::extensions_str)
+ .flatten()
+ .collect::>(),
+ )
+ .pick_file()
+ .context("no input image selected")?,
+ FileDialog::new()
+ .set_title("Output image")
+ .set_directory(working_dir())
+ .save_file()
+ .context("no output filename provided")?,
+ )
+ }
+}
fn main() -> Result<()> {
- let mut bmp = open("bmp/bigsample.bmp")?;
- for y in 0..bmp.get_height() {
- for x in 0..bmp.get_width() {
- if x > 100 && x < 300 {
- bmp.set_pixel(
- x,
- y,
- sample_to_rgb(rgb_to_sample::(bmp.get_pixel(x, y))),
- );
- }
- }
- }
- bmp.to_writer(&mut File::create("bmp/out.bmp")?)?;
+ let dp = DataPath::new(Files::prompt()?);
+ let img = RawData::::new(
+ ImageReader::open(dp.files.input)?
+ .decode()
+ .context("can't use this picture")?
+ .into_rgb8(),
+ );
+ let processed = RawData::::new(
+ RgbImage::from_raw(
+ img.width(),
+ img.height(),
+ img.par_chunks_exact(3)
+ .progress_count((img.width() * img.height()).into())
+ .with_style(ProgressStyle::with_template(
+ "[{eta}] {bar:40.green/red} {pos}/{len} pixels",
+ )?)
+ .map(|px: &[u8]| (px[2] as i32) | ((px[1] as i32) << 8) | ((px[0] as i32) << 16))
+ .map(U24::new_unchecked)
+ .map(|x| uparc(x.to_sample()))
+ .flat_map(|sample: f32| {
+ let rgb: U24 = sample.to_sample();
+ let rgo = ((rgb) >> 8.into()) << 8.into();
+ let roo = ((rgo) >> 16.into()) << 16.into();
+ [
+ ((roo) >> 16.into()).inner() as u8,
+ ((rgo - roo) >> 8.into()).inner() as u8,
+ (rgb - rgo).inner() as u8,
+ ]
+ })
+ .collect(),
+ )
+ .unwrap(),
+ );
+ processed.save(dp.files.output)?;
+
+ // let bytes: Vec = (1u8..7).into_iter().collect();
+ // assert_eq!(
+ // bytes,
+ // bytes
+ // .chunks_exact(3)
+ // .map(|px: &[u8]| f64::from_ne_bytes(array::from_fn(|i| px[i % px.len()])))
+ // .flat_map(|px: f64| -> [u8; 3] {
+ // let px = px.to_ne_bytes();
+ // array::from_fn(move |i| px[i])
+ // })
+ // .collect::>()
+ // );
Ok(())
}
-fn rgb_to_sample>(pix: Pixel) -> T {
- (((U24::from(pix.r)) << 16.into()) | (U24::from(pix.g) << 8.into()) | U24::from(pix.b))
- .to_sample()
-}
-
-fn sample_to_rgb>(sample: T) -> Pixel {
- let rgb: U24 = sample.to_sample();
- let rgo = ((rgb) >> 8.into()) << 8.into();
- let roo = ((rgo) >> 16.into()) << 16.into();
- Pixel::new(
- ((roo) >> 16.into()).inner() as u8,
- ((rgo - roo) >> 8.into()).inner() as u8,
- (rgb - rgo).inner() as u8,
- )
-}
-
-#[cfg(test)]
-mod tests {
- use bmp::Pixel;
- use dasp_sample::{Sample, I24, I48, U24, U48};
-
- use super::{rgb_to_sample, sample_to_rgb};
-
- #[test]
- fn sample_convert_consistency() {
- let original = U24::from(42);
- assert_eq!(original.clone(), original.to_sample::().to_sample())
- }
-
- #[test]
- fn rgb2s2rgb_type_consistency() {
- let pix = Pixel::new(45, 18, 143);
- assert_eq!(
- sample_to_rgb(rgb_to_sample::(pix)),
- sample_to_rgb(rgb_to_sample::(pix))
- );
- assert_eq!(
- sample_to_rgb(rgb_to_sample::(pix)),
- sample_to_rgb(rgb_to_sample::(pix))
- );
- assert_eq!(
- sample_to_rgb(rgb_to_sample::(pix)),
- sample_to_rgb(rgb_to_sample::(pix))
- );
- }
-
- #[test]
- fn rgb2s2rgb() {
- let pix = Pixel::new(45, 18, 143);
- assert_eq!(pix, (sample_to_rgb(rgb_to_sample::(pix))));
- }
+fn working_dir() -> PathBuf {
+ picture_dir()
+ .or(download_dir())
+ .or(home_dir())
+ .unwrap_or("/".into())
}
diff --git a/bent/Cargo.toml b/bent/Cargo.toml
index 7c3fd50..e00aed4 100644
--- a/bent/Cargo.toml
+++ b/bent/Cargo.toml
@@ -10,4 +10,11 @@ keywords.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+[lib]
+crate-type = ["cdylib"]
+
[dependencies]
+wasm-bindgen = "0.2"
+
+[profile.release]
+lto = true
diff --git a/bent/LICENSE b/bent/LICENSE
new file mode 100644
index 0000000..68a49da
--- /dev/null
+++ b/bent/LICENSE
@@ -0,0 +1,24 @@
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to
diff --git a/bent/assets/bent.svg b/bent/assets/bent.svg
new file mode 100644
index 0000000..60b8b98
--- /dev/null
+++ b/bent/assets/bent.svg
@@ -0,0 +1,12 @@
+
\ No newline at end of file
diff --git a/bent/assets/path-generator.py b/bent/assets/path-generator.py
new file mode 100644
index 0000000..1af8d68
--- /dev/null
+++ b/bent/assets/path-generator.py
@@ -0,0 +1,15 @@
+p = lambda x: print(x, end=" ")
+
+
+def line(coords: tuple[tuple[int]]):
+ p(f"M {coords[0][0]} {coords[0][1]} L {coords[1][0]} {coords[1][1]}")
+
+
+n = 100
+line(((0, 0), (n, n)))
+line(((n, 0), (0, n)))
+for i in range(1, n):
+ line(((i, 0), (n - i, n)))
+ line(((0, i), (n, n - i)))
+
+print()
diff --git a/bent/index.html b/bent/index.html
new file mode 100644
index 0000000..6a253de
--- /dev/null
+++ b/bent/index.html
@@ -0,0 +1,36 @@
+
+
+
+
+
+ BENT
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/bent/src/js.rs b/bent/src/js.rs
new file mode 100644
index 0000000..a78e4b1
--- /dev/null
+++ b/bent/src/js.rs
@@ -0,0 +1,7 @@
+use wasm_bindgen::prelude::*;
+
+#[wasm_bindgen]
+extern "C" {
+ #[wasm_bindgen(js_namespace = console)]
+ pub fn log(a: &str);
+}
diff --git a/bent/src/lib.rs b/bent/src/lib.rs
new file mode 100644
index 0000000..d26acc3
--- /dev/null
+++ b/bent/src/lib.rs
@@ -0,0 +1,10 @@
+use wasm_bindgen::prelude::*;
+#[allow(unused_macros)]
+#[macro_use]
+pub mod macros;
+pub mod js;
+
+#[wasm_bindgen]
+pub fn greet(name: &str) {
+ println!("Hello, {name}!");
+}
diff --git a/bent/src/macros.rs b/bent/src/macros.rs
new file mode 100644
index 0000000..7d9c941
--- /dev/null
+++ b/bent/src/macros.rs
@@ -0,0 +1,27 @@
+macro_rules! println {
+ ($($t:tt)*) => (crate::js::log(&format_args!($($t)*).to_string()))
+}
+
+macro_rules! dbg {
+ // NOTE: We cannot use `concat!` to make a static string as a format argument
+ // of `eprintln!` because `file!` could contain a `{` or
+ // `$val` expression could be a block (`{ .. }`), in which case the `eprintln!`
+ // will be malformed.
+ () => {
+ println!("[{}:{}:{}]", file!(), line!(), column!())
+ };
+ ($val:expr $(,)?) => {
+ // Use of `match` here is intentional because it affects the lifetimes
+ // of temporaries - https://stackoverflow.com/a/48732525/1063961
+ match $val {
+ tmp => {
+ println!("[{}:{}:{}] {} = {:#?}",
+ file!(), line!(), column!(), stringify!($val), &tmp);
+ tmp
+ }
+ }
+ };
+ ($($val:expr),+ $(,)?) => {
+ ($(dbg!($val)),+,)
+ };
+}
diff --git a/bent/src/main.rs b/bent/src/main.rs
deleted file mode 100644
index e7a11a9..0000000
--- a/bent/src/main.rs
+++ /dev/null
@@ -1,3 +0,0 @@
-fn main() {
- println!("Hello, world!");
-}
diff --git a/bingus/Cargo.toml b/bingus/Cargo.toml
index 8027b64..5f28930 100644
--- a/bingus/Cargo.toml
+++ b/bingus/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "bingus"
-version = "0.1.0"
+version = "0.1.2"
edition.workspace = true
license.workspace = true
description.workspace = true
@@ -10,4 +10,4 @@ keywords.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
-dasp_sample = { workspace = true }
+derive-new = { workspace = true }
diff --git a/bingus/src/lib.rs b/bingus/src/lib.rs
index 51ab99d..e295326 100644
--- a/bingus/src/lib.rs
+++ b/bingus/src/lib.rs
@@ -1,43 +1,2 @@
-use dasp_sample::{FromSample, Sample, U24, U48};
-
-pub trait IntoSample
-where
- Self: Sized,
-{
- fn _prepare(self) -> F;
- fn into_sample>(self) -> S {
- Sample::from_sample(self._prepare())
- }
-}
-
-impl IntoSample for u8 {
- fn _prepare(self) -> u8 {
- self
- }
-}
-
-impl IntoSample for [u8; 2] {
- fn _prepare(self) -> u16 {
- ((self[0]._prepare() as u16) << 8) | self[1] as u16
- }
-}
-
-impl IntoSample for [u8; 3] {
- fn _prepare(self) -> U24 {
- (U24::from([self[0], self[1]]._prepare()) << 8.into()) | U24::from(self[2])
- }
-}
-
-impl IntoSample for [u8; 4] {
- fn _prepare(self) -> u32 {
- (([self[0], self[1], self[2]]._prepare().inner() as u32) << 8) | self[3] as u32
- }
-}
-
-impl IntoSample for [u8; 6] {
- fn _prepare(self) -> U48 {
- ((U48::from([self[0], self[1], self[2], self[3]]._prepare()) << 16u8.into())
- | (U48::from(self[4]) << 8u8.into()))
- | U48::from(self[5])
- }
-}
+#![forbid(unused_crate_dependencies)]
+pub mod rawdata;
diff --git a/bingus/src/rawdata.rs b/bingus/src/rawdata.rs
new file mode 100644
index 0000000..d0ac87b
--- /dev/null
+++ b/bingus/src/rawdata.rs
@@ -0,0 +1,36 @@
+use std::ops::Deref;
+
+use derive_new::new;
+
+pub type Bytes = [u8];
+
+#[derive(new)]
+pub struct RawData>(D);
+
+impl From> for Vec
+where
+ D: Deref,
+{
+ fn from(value: RawData) -> Self {
+ value.to_owned()
+ }
+}
+
+impl Clone for RawData
+where
+ D: Clone + Deref,
+{
+ fn clone(&self) -> Self {
+ Self(self.deref().clone())
+ }
+}
+
+impl Deref for RawData
+where
+ D: Deref,
+{
+ type Target = D;
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}