Compare commits

...

17 Commits

Author SHA1 Message Date
Ponj b6eb5720c9
update image 2024-07-27 08:41:34 +02:00
Ponj 2190440b3f
Merge branch 'fundsp' 2024-07-27 08:40:02 +02:00
Ponj 95aaed66e7
vanilla wasm_bindgen bent 2024-07-27 08:35:40 +02:00
Ponj d994f316a7
add licenses 2024-07-27 07:26:19 +02:00
Ponj ff3a6ee5fc
clean bingus deps 2024-07-23 17:53:07 +02:00
Ponj f8ed6d2ce1
prepare for cargo publish 2024-07-23 17:50:26 +02:00
Ponj ba055093be
image filter 2024-07-23 17:47:18 +02:00
Ponj 3315342aa5
image fix came out 2024-07-20 21:13:30 +02:00
Ponj c56b0b6227
simplify lib towards simple wrapper 2024-07-14 17:35:44 +02:00
Ponj a0d82499c0
reorganize lib, use it in the funny zone 2024-07-14 07:57:40 +02:00
Ponj 825bcfa5a1
RawImage 2024-07-13 10:05:27 +02:00
Ponj e34287df9e
RawData, clippy 2024-07-13 09:07:50 +02:00
Ponj 5d39e227b6
file picker, progress bar, perf enhancements 2024-07-11 17:45:55 +02:00
Ponj 5c2607b574
it works!! 2024-07-10 18:58:11 +02:00
Ponj 14c98f484b
reorganize bingus 2024-07-07 12:12:58 +02:00
Ponj 1dec7aad56
yay! macro! 2024-07-07 08:53:56 +02:00
Ponj 0194f60ca3
finally a perfect ToSample trait! 2024-07-07 08:19:23 +02:00
17 changed files with 350 additions and 121 deletions

2
.gitignore vendored
View File

@ -1,3 +1,3 @@
/target
*.lock
bmp/out.bmp
bmp/out.*

View File

@ -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"

24
LICENSE Normal file
View File

@ -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 <http://unlicense.org/>

View File

@ -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 }

View File

@ -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::{ImageFormat, ImageReader, 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<Self> {
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<PathBuf> {
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<Self> {
Files::try_new(
FileDialog::new()
.set_title("Input image")
.set_directory(working_dir())
.add_filter(
"images",
&ImageFormat::all()
.map(ImageFormat::extensions_str)
.flatten()
.collect::<Vec<&'static &'static str>>(),
)
.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::<f64>(bmp.get_pixel(x, y))),
);
}
}
}
bmp.to_writer(&mut File::create("bmp/out.bmp")?)?;
let dp = DataPath::new(Files::prompt()?);
let img = RawData::<RgbImage>::new(
ImageReader::open(dp.files.input)?
.decode()
.context("can't use this picture")?
.into_rgb8(),
);
let processed = RawData::<RgbImage>::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<u8> = (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::<Vec<u8>>()
// );
Ok(())
}
fn rgb_to_sample<T: Sample + FromSample<U24>>(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<T: Sample + ToSample<U24>>(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::<f64>().to_sample())
}
#[test]
fn rgb2s2rgb_type_consistency() {
let pix = Pixel::new(45, 18, 143);
assert_eq!(
sample_to_rgb(rgb_to_sample::<f32>(pix)),
sample_to_rgb(rgb_to_sample::<f64>(pix))
);
assert_eq!(
sample_to_rgb(rgb_to_sample::<I24>(pix)),
sample_to_rgb(rgb_to_sample::<I48>(pix))
);
assert_eq!(
sample_to_rgb(rgb_to_sample::<U24>(pix)),
sample_to_rgb(rgb_to_sample::<U48>(pix))
);
}
#[test]
fn rgb2s2rgb() {
let pix = Pixel::new(45, 18, 143);
assert_eq!(pix, (sample_to_rgb(rgb_to_sample::<f64>(pix))));
}
fn working_dir() -> PathBuf {
picture_dir()
.or(download_dir())
.or(home_dir())
.unwrap_or("/".into())
}

View File

@ -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

24
bent/LICENSE Normal file
View File

@ -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 <http://unlicense.org/>

12
bent/assets/bent.svg Normal file
View File

@ -0,0 +1,12 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<style>
path {
stroke: blue;
stroke-width: .2;
}
</style>
<path
d="M 0 0 L 100 100 M 100 0 L 0 100 M 1 0 L 99 100 M 0 1 L 100 99 M 2 0 L 98 100 M 0 2 L 100 98 M 3 0 L 97 100 M 0 3 L 100 97 M 4 0 L 96 100 M 0 4 L 100 96 M 5 0 L 95 100 M 0 5 L 100 95 M 6 0 L 94 100 M 0 6 L 100 94 M 7 0 L 93 100 M 0 7 L 100 93 M 8 0 L 92 100 M 0 8 L 100 92 M 9 0 L 91 100 M 0 9 L 100 91 M 10 0 L 90 100 M 0 10 L 100 90 M 11 0 L 89 100 M 0 11 L 100 89 M 12 0 L 88 100 M 0 12 L 100 88 M 13 0 L 87 100 M 0 13 L 100 87 M 14 0 L 86 100 M 0 14 L 100 86 M 15 0 L 85 100 M 0 15 L 100 85 M 16 0 L 84 100 M 0 16 L 100 84 M 17 0 L 83 100 M 0 17 L 100 83 M 18 0 L 82 100 M 0 18 L 100 82 M 19 0 L 81 100 M 0 19 L 100 81 M 20 0 L 80 100 M 0 20 L 100 80 M 21 0 L 79 100 M 0 21 L 100 79 M 22 0 L 78 100 M 0 22 L 100 78 M 23 0 L 77 100 M 0 23 L 100 77 M 24 0 L 76 100 M 0 24 L 100 76 M 25 0 L 75 100 M 0 25 L 100 75 M 26 0 L 74 100 M 0 26 L 100 74 M 27 0 L 73 100 M 0 27 L 100 73 M 28 0 L 72 100 M 0 28 L 100 72 M 29 0 L 71 100 M 0 29 L 100 71 M 30 0 L 70 100 M 0 30 L 100 70 M 31 0 L 69 100 M 0 31 L 100 69 M 32 0 L 68 100 M 0 32 L 100 68 M 33 0 L 67 100 M 0 33 L 100 67 M 34 0 L 66 100 M 0 34 L 100 66 M 35 0 L 65 100 M 0 35 L 100 65 M 36 0 L 64 100 M 0 36 L 100 64 M 37 0 L 63 100 M 0 37 L 100 63 M 38 0 L 62 100 M 0 38 L 100 62 M 39 0 L 61 100 M 0 39 L 100 61 M 40 0 L 60 100 M 0 40 L 100 60 M 41 0 L 59 100 M 0 41 L 100 59 M 42 0 L 58 100 M 0 42 L 100 58 M 43 0 L 57 100 M 0 43 L 100 57 M 44 0 L 56 100 M 0 44 L 100 56 M 45 0 L 55 100 M 0 45 L 100 55 M 46 0 L 54 100 M 0 46 L 100 54 M 47 0 L 53 100 M 0 47 L 100 53 M 48 0 L 52 100 M 0 48 L 100 52 M 49 0 L 51 100 M 0 49 L 100 51 M 50 0 L 50 100 M 0 50 L 100 50 M 51 0 L 49 100 M 0 51 L 100 49 M 52 0 L 48 100 M 0 52 L 100 48 M 53 0 L 47 100 M 0 53 L 100 47 M 54 0 L 46 100 M 0 54 L 100 46 M 55 0 L 45 100 M 0 55 L 100 45 M 56 0 L 44 100 M 0 56 L 100 44 M 57 0 L 43 100 M 0 57 L 100 43 M 58 0 L 42 100 M 0 58 L 100 42 M 59 0 L 41 100 M 0 59 L 100 41 M 60 0 L 40 100 M 0 60 L 100 40 M 61 0 L 39 100 M 0 61 L 100 39 M 62 0 L 38 100 M 0 62 L 100 38 M 63 0 L 37 100 M 0 63 L 100 37 M 64 0 L 36 100 M 0 64 L 100 36 M 65 0 L 35 100 M 0 65 L 100 35 M 66 0 L 34 100 M 0 66 L 100 34 M 67 0 L 33 100 M 0 67 L 100 33 M 68 0 L 32 100 M 0 68 L 100 32 M 69 0 L 31 100 M 0 69 L 100 31 M 70 0 L 30 100 M 0 70 L 100 30 M 71 0 L 29 100 M 0 71 L 100 29 M 72 0 L 28 100 M 0 72 L 100 28 M 73 0 L 27 100 M 0 73 L 100 27 M 74 0 L 26 100 M 0 74 L 100 26 M 75 0 L 25 100 M 0 75 L 100 25 M 76 0 L 24 100 M 0 76 L 100 24 M 77 0 L 23 100 M 0 77 L 100 23 M 78 0 L 22 100 M 0 78 L 100 22 M 79 0 L 21 100 M 0 79 L 100 21 M 80 0 L 20 100 M 0 80 L 100 20 M 81 0 L 19 100 M 0 81 L 100 19 M 82 0 L 18 100 M 0 82 L 100 18 M 83 0 L 17 100 M 0 83 L 100 17 M 84 0 L 16 100 M 0 84 L 100 16 M 85 0 L 15 100 M 0 85 L 100 15 M 86 0 L 14 100 M 0 86 L 100 14 M 87 0 L 13 100 M 0 87 L 100 13 M 88 0 L 12 100 M 0 88 L 100 12 M 89 0 L 11 100 M 0 89 L 100 11 M 90 0 L 10 100 M 0 90 L 100 10 M 91 0 L 9 100 M 0 91 L 100 9 M 92 0 L 8 100 M 0 92 L 100 8 M 93 0 L 7 100 M 0 93 L 100 7 M 94 0 L 6 100 M 0 94 L 100 6 M 95 0 L 5 100 M 0 95 L 100 5 M 96 0 L 4 100 M 0 96 L 100 4 M 97 0 L 3 100 M 0 97 L 100 3 M 98 0 L 2 100 M 0 98 L 100 2 M 99 0 L 1 100 M 0 99 L 100 1"
mask="url(#Mask)" />
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -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()

36
bent/index.html Normal file
View File

@ -0,0 +1,36 @@
<!doctype html>
<html lang="en-US">
<head>
<meta charset="utf-8" />
<title>BENT</title>
<link rel="shortcut icon" href="assets/bent.svg" type="image/svg">
<link rel="stylesheet" href="https://cdn.simplecss.org/simple.min.css">
</head>
<script type="module">
import init, { greet } from "./pkg/bent.js";
init().then(() => {
document.getElementById("greet-button").onclick = () => {
const name = document.getElementById("name").value;
greet(name);
};
});
</script>
<body>
<header>
<h1>BENT</h1>
</header>
<form onsubmit="return false;">
<p>
<label>Enter your name:</label>
<input type="text" id="name" />
</p>
<button id="greet-button">Greet</button>
<button type="reset">Reset</button>
</form>
</body>
</html>

7
bent/src/js.rs Normal file
View File

@ -0,0 +1,7 @@
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]
pub fn log(a: &str);
}

10
bent/src/lib.rs Normal file
View File

@ -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}!");
}

27
bent/src/macros.rs Normal file
View File

@ -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)),+,)
};
}

View File

@ -1,3 +0,0 @@
fn main() {
println!("Hello, world!");
}

View File

@ -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 }

View File

@ -1,43 +1,2 @@
use dasp_sample::{FromSample, Sample, U24, U48};
pub trait IntoSample<F>
where
Self: Sized,
{
fn _prepare(self) -> F;
fn into_sample<S: Sample + FromSample<F>>(self) -> S {
Sample::from_sample(self._prepare())
}
}
impl IntoSample<u8> for u8 {
fn _prepare(self) -> u8 {
self
}
}
impl IntoSample<u16> for [u8; 2] {
fn _prepare(self) -> u16 {
((self[0]._prepare() as u16) << 8) | self[1] as u16
}
}
impl IntoSample<U24> for [u8; 3] {
fn _prepare(self) -> U24 {
(U24::from([self[0], self[1]]._prepare()) << 8.into()) | U24::from(self[2])
}
}
impl IntoSample<u32> for [u8; 4] {
fn _prepare(self) -> u32 {
(([self[0], self[1], self[2]]._prepare().inner() as u32) << 8) | self[3] as u32
}
}
impl IntoSample<U48> 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;

36
bingus/src/rawdata.rs Normal file
View File

@ -0,0 +1,36 @@
use std::ops::Deref;
use derive_new::new;
pub type Bytes = [u8];
#[derive(new)]
pub struct RawData<D: Deref<Target = Bytes>>(D);
impl<D> From<RawData<D>> for Vec<u8>
where
D: Deref<Target = Bytes>,
{
fn from(value: RawData<D>) -> Self {
value.to_owned()
}
}
impl<D> Clone for RawData<D>
where
D: Clone + Deref<Target = Bytes>,
{
fn clone(&self) -> Self {
Self(self.deref().clone())
}
}
impl<D> Deref for RawData<D>
where
D: Deref<Target = Bytes>,
{
type Target = D;
fn deref(&self) -> &Self::Target {
&self.0
}
}