some basic operations
This commit is contained in:
parent
7e974d42ae
commit
4285b7a966
14 changed files with 468 additions and 39 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,2 +1,3 @@
|
||||||
/target
|
/target
|
||||||
/.direnv
|
/.direnv
|
||||||
|
*.png
|
||||||
|
|
|
||||||
78
Cargo.lock
generated
78
Cargo.lock
generated
|
|
@ -2,6 +2,84 @@
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 4
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "adler2"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crc32fast"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fdeflate"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
|
||||||
|
dependencies = [
|
||||||
|
"simd-adler32",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "flate2"
|
||||||
|
version = "1.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c"
|
||||||
|
dependencies = [
|
||||||
|
"crc32fast",
|
||||||
|
"miniz_oxide",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "imgsyn"
|
name = "imgsyn"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"png",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "miniz_oxide"
|
||||||
|
version = "0.8.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
|
||||||
|
dependencies = [
|
||||||
|
"adler2",
|
||||||
|
"simd-adler32",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "png"
|
||||||
|
version = "0.18.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "97baced388464909d42d89643fe4361939af9b7ce7a31ee32a168f832a70f2a0"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"crc32fast",
|
||||||
|
"fdeflate",
|
||||||
|
"flate2",
|
||||||
|
"miniz_oxide",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "simd-adler32"
|
||||||
|
version = "0.3.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2"
|
||||||
|
|
|
||||||
|
|
@ -4,3 +4,4 @@ version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
png = "0.18.0"
|
||||||
|
|
|
||||||
|
|
@ -8,5 +8,6 @@ pkgs.mkShell {
|
||||||
ffmpeg
|
ffmpeg
|
||||||
imagemagick
|
imagemagick
|
||||||
rust-analyzer
|
rust-analyzer
|
||||||
|
cargo-watch
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
25
src/assist.rs
Normal file
25
src/assist.rs
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
use std::f32::consts::*;
|
||||||
|
|
||||||
|
use crate::Canvas;
|
||||||
|
|
||||||
|
pub fn polar(x: f32, y: f32, meta: Canvas) -> (f32, f32) {
|
||||||
|
let x = x - 0.5;
|
||||||
|
let y = y - 0.5;
|
||||||
|
let r = (x * x + y * y).sqrt();
|
||||||
|
let mut phi = f32::atan2(y, x) + PI / 2.;
|
||||||
|
phi = phi.rem_euclid(TAU);
|
||||||
|
(r * 2. / corner_distance(meta), phi / TAU)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn axial(r: f32, phi: f32, meta: Canvas) -> (f32, f32) {
|
||||||
|
let phi = phi * TAU - PI / 2.;
|
||||||
|
let r = r / 2. * corner_distance(meta);
|
||||||
|
let x = r * phi.cos();
|
||||||
|
let y = r * phi.sin();
|
||||||
|
(x + 0.5, y + 0.5)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn corner_distance(meta: Canvas) -> f32 {
|
||||||
|
let corner_distance = (meta.width * meta.width + meta.height * meta.height).sqrt();
|
||||||
|
corner_distance / meta.width.max(meta.height)
|
||||||
|
}
|
||||||
|
|
@ -2,19 +2,18 @@ use crate::{ImgSyn, pixel::Pixel};
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq)]
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
pub struct Canvas {
|
pub struct Canvas {
|
||||||
pub width: u32,
|
pub width: f32,
|
||||||
pub height: u32,
|
pub height: f32,
|
||||||
pub default_px: Pixel,
|
pub default_px: Pixel,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub struct PaintedCanvas {
|
pub struct PaintedCanvas {
|
||||||
pub meta: Canvas,
|
|
||||||
pub array: Vec<Vec<Pixel>>,
|
pub array: Vec<Vec<Pixel>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Canvas {
|
impl Canvas {
|
||||||
pub fn new(width: u32, height: u32, default_px: Pixel) -> Self {
|
pub fn new(width: f32, height: f32, default_px: Pixel) -> Self {
|
||||||
Self {
|
Self {
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
|
|
@ -32,25 +31,34 @@ impl ImgSyn for Canvas {
|
||||||
impl PaintedCanvas {
|
impl PaintedCanvas {
|
||||||
pub fn new(meta @ Canvas { width, height, .. }: Canvas, function: impl ImgSyn) -> Self {
|
pub fn new(meta @ Canvas { width, height, .. }: Canvas, function: impl ImgSyn) -> Self {
|
||||||
Self {
|
Self {
|
||||||
meta,
|
array: (0..height as u32)
|
||||||
array: (0..width)
|
.map(|y| y as f32 / height)
|
||||||
.map(|x| x as f32 / width as f32)
|
.map(|y| {
|
||||||
.map(|x| {
|
(0..width as u32)
|
||||||
(0..height)
|
.map(|x| x as f32 / width)
|
||||||
.map(|y| y as f32 / height as f32)
|
.map(|x| function.getpx(meta, x, y))
|
||||||
.map(|y| function.getpx(meta, x, y))
|
|
||||||
.collect()
|
.collect()
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn rgba255(&self) -> Vec<u8> {
|
||||||
|
self.array
|
||||||
|
.iter()
|
||||||
|
.flat_map(|row| row.iter().flat_map(|pix| pix.rgba255()))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ImgSyn for PaintedCanvas {
|
impl ImgSyn for PaintedCanvas {
|
||||||
fn getpx(&self, meta: Canvas, x: f32, y: f32) -> Pixel {
|
fn getpx(&self, meta: Canvas, x: f32, y: f32) -> Pixel {
|
||||||
// float coords -> int coords
|
// float coords -> int coords
|
||||||
let x = (x * meta.width as f32).round() as usize;
|
let x = (x * meta.width).round() as usize;
|
||||||
let y = (y * meta.height as f32).round() as usize;
|
let y = (y * meta.height).round() as usize;
|
||||||
self.array[x][y]
|
self.array[y][x]
|
||||||
|
}
|
||||||
|
fn paint(self, _meta: Canvas) -> PaintedCanvas {
|
||||||
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
158
src/lib.rs
158
src/lib.rs
|
|
@ -1,5 +1,6 @@
|
||||||
|
pub mod assist;
|
||||||
mod canvas;
|
mod canvas;
|
||||||
pub mod ops;
|
mod ops;
|
||||||
mod pixel;
|
mod pixel;
|
||||||
mod pixfn;
|
mod pixfn;
|
||||||
|
|
||||||
|
|
@ -7,30 +8,171 @@ pub use canvas::*;
|
||||||
pub use pixel::*;
|
pub use pixel::*;
|
||||||
pub use pixfn::*;
|
pub use pixfn::*;
|
||||||
|
|
||||||
use crate::ops::ImgSynMap;
|
use crate::ops::{
|
||||||
|
ImgSynArea, ImgSynAxial, ImgSynMap, ImgSynMapArea, ImgSynPolar, ImgSynRect, ImgSynStretched,
|
||||||
|
ImgSynUnmapRect,
|
||||||
|
};
|
||||||
|
|
||||||
pub trait ImgSynPixFn: Clone {
|
pub trait ImgSynPixFn<T>: Clone {
|
||||||
fn run(&self, meta: Canvas, x: f32, y: f32, pixel: Pixel) -> Pixel;
|
fn run(&self, meta: Canvas, x: f32, y: f32, pixel: Pixel) -> T;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ImgSyn: Clone {
|
pub trait ImgSyn: Clone {
|
||||||
fn getpx(&self, meta: Canvas, x: f32, y: f32) -> Pixel;
|
fn getpx(&self, meta: Canvas, x: f32, y: f32) -> Pixel;
|
||||||
fn map<F: ImgSynPixFn>(self, f: F) -> ImgSynMap<Self, F> {
|
fn map<F: ImgSynPixFn<Pixel>>(self, f: F) -> ImgSynMap<Self, F> {
|
||||||
ImgSynMap(self, f)
|
ImgSynMap(self, f)
|
||||||
}
|
}
|
||||||
|
fn area<C: ImgSynPixFn<bool>, ImgB: ImgSyn, F: FnOnce(Self) -> ImgB>(
|
||||||
|
self,
|
||||||
|
condition: C,
|
||||||
|
f: F,
|
||||||
|
) -> ImgSynArea<Self, ImgB, C> {
|
||||||
|
ImgSynArea(self.clone(), f(self), condition)
|
||||||
|
}
|
||||||
|
fn mapped_rect<ImgB: ImgSyn, F: FnOnce(ImgSynUnmapRect<Self>) -> ImgB>(
|
||||||
|
self,
|
||||||
|
begin_x: f32,
|
||||||
|
begin_y: f32,
|
||||||
|
size_x: f32,
|
||||||
|
size_y: f32,
|
||||||
|
f: F,
|
||||||
|
) -> ImgSynRect<Self, ImgB> {
|
||||||
|
let rect = (begin_x, begin_y, size_x, size_y);
|
||||||
|
ImgSynRect(self.clone(), f(ImgSynUnmapRect(self, rect)), rect)
|
||||||
|
}
|
||||||
|
fn add_image<ImgB: ImgSyn>(
|
||||||
|
self,
|
||||||
|
begin_x: f32,
|
||||||
|
begin_y: f32,
|
||||||
|
size_x: f32,
|
||||||
|
size_y: f32,
|
||||||
|
img: ImgB,
|
||||||
|
) -> ImgSynRect<Self, ImgB> {
|
||||||
|
let rect = (begin_x, begin_y, size_x, size_y);
|
||||||
|
ImgSynRect(self, img, rect)
|
||||||
|
}
|
||||||
|
fn map_area<C: ImgSynPixFn<bool>, F: ImgSynPixFn<Pixel>>(
|
||||||
|
self,
|
||||||
|
condition: C,
|
||||||
|
f: F,
|
||||||
|
) -> ImgSynMapArea<Self, C, F> {
|
||||||
|
ImgSynMapArea(self, condition, f)
|
||||||
|
}
|
||||||
|
/// Makes future operations work on polar coordinates when terminated by .axial()
|
||||||
|
///
|
||||||
|
/// **MUST** be used before a subsequent .axial()
|
||||||
|
fn polar(self) -> ImgSynAxial<Self> {
|
||||||
|
ImgSynAxial(self)
|
||||||
|
}
|
||||||
|
/// Makes future operations work on axial coordinates when .polar() was used previously
|
||||||
|
///
|
||||||
|
/// **MUST** be used after .polar()
|
||||||
|
fn axial(self) -> ImgSynPolar<Self> {
|
||||||
|
ImgSynPolar(self)
|
||||||
|
}
|
||||||
|
fn stretched_by(self, x: f32, y: f32) -> ImgSynStretched<Self> {
|
||||||
|
ImgSynStretched(self, (x, y))
|
||||||
|
}
|
||||||
fn paint(self, meta: Canvas) -> PaintedCanvas {
|
fn paint(self, meta: Canvas) -> PaintedCanvas {
|
||||||
PaintedCanvas::new(meta, self)
|
PaintedCanvas::new(meta, self)
|
||||||
}
|
}
|
||||||
|
fn rgba255(self, meta: Canvas) -> Vec<u8> {
|
||||||
|
let painted = self.paint(meta);
|
||||||
|
PaintedCanvas::rgba255(&painted)
|
||||||
|
}
|
||||||
|
fn png(self, meta: Canvas) -> Vec<u8> {
|
||||||
|
let painted = self.paint(meta);
|
||||||
|
let mut data = Vec::new();
|
||||||
|
{
|
||||||
|
let mut encoder = png::Encoder::new(&mut data, meta.width as u32, meta.height as u32);
|
||||||
|
encoder.set_color(png::ColorType::Rgba);
|
||||||
|
encoder.set_depth(png::BitDepth::Eight);
|
||||||
|
let mut writer = encoder.write_header().unwrap();
|
||||||
|
writer.write_image_data(&painted.rgba255(meta)).unwrap();
|
||||||
|
writer.finish().unwrap();
|
||||||
|
}
|
||||||
|
data
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
use crate::{Canvas, ImgSyn, PixFnFloat, Pixel};
|
use crate::{Canvas, ImgSyn, PixFnFloat, Pixel};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test() {
|
fn test() {
|
||||||
Canvas::new(100, 100, Pixel::new_black())
|
let canvas = Canvas::new(1000., 1000., Pixel::new_black());
|
||||||
.map(PixFnFloat(|_, x, y, pix| pix.with_r(x).with_g(y)))
|
fs::write(
|
||||||
.map(PixFnFloat(|_, _, _, pix| pix.with_b(1.0)));
|
"test.png",
|
||||||
|
canvas
|
||||||
|
.polar()
|
||||||
|
.map(|_, y: f32, pix: Pixel| pix.with_r((y - 0.5).abs() * 2.))
|
||||||
|
.map(|_, y, pix: Pixel| pix.with_b(y))
|
||||||
|
.axial()
|
||||||
|
.map(PixFnFloat(|_, _, y, pix| pix.with_g(y)))
|
||||||
|
.map_area(
|
||||||
|
PixFnFloat(|_, x, y, _| x < 0.2 && y < 0.2),
|
||||||
|
PixFnFloat(|_, _, _, pix| pix.with_g(1.0)),
|
||||||
|
)
|
||||||
|
.area(PixFnFloat(|_, _, _, px| px.g > 0.8 && px.r < 0.3), |img| {
|
||||||
|
img.polar()
|
||||||
|
.map(PixFnFloat(|_, x, y, pix| pix.with_r(x).with_b(1. - y)))
|
||||||
|
.axial()
|
||||||
|
})
|
||||||
|
.mapped_rect(0.3, 0.3, 0.4, 0.4, |img| {
|
||||||
|
img.map(PixFnFloat(|_, x, y, px| {
|
||||||
|
let thickness = 0.01;
|
||||||
|
if !(thickness..=1. - thickness).contains(&x)
|
||||||
|
|| !(thickness..=1. - thickness).contains(&y)
|
||||||
|
{
|
||||||
|
return Pixel::new_white();
|
||||||
|
}
|
||||||
|
px
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
.paint(canvas)
|
||||||
|
.png(canvas),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn graph() {
|
||||||
|
let canvas = Canvas::new(1000., 1000., Pixel::new_white());
|
||||||
|
fs::write(
|
||||||
|
"graph.png",
|
||||||
|
canvas
|
||||||
|
.add_image(
|
||||||
|
0.1,
|
||||||
|
0.0,
|
||||||
|
0.9,
|
||||||
|
0.9,
|
||||||
|
Canvas::new(900., 900., Pixel::new_black()).map_area(
|
||||||
|
|x, y, _| x < 0.7 || y > 0.3,
|
||||||
|
|x, y, px: Pixel| px.with_r(x).with_g(1. - y),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.map_area(
|
||||||
|
|x, y, _| {
|
||||||
|
let t = 0.004;
|
||||||
|
(0.1 - t..0.1 + t).contains(&x) || (0.9 - t..0.9 + t).contains(&y)
|
||||||
|
},
|
||||||
|
|_, _, px: Pixel| px.with_rgb(1.0, 0.0, 0.0),
|
||||||
|
)
|
||||||
|
.mapped_rect(0.05, 0.90, 0.05, 0.05, |img| {
|
||||||
|
img.polar()
|
||||||
|
.map_area(
|
||||||
|
|r: f32, phi, _| (r - 0.3).abs() < 0.04,
|
||||||
|
|_, _, px: Pixel| px.with_rgb_of(0.0),
|
||||||
|
)
|
||||||
|
.axial()
|
||||||
|
.stretched_by(1.0, 1.5)
|
||||||
|
})
|
||||||
|
.paint(canvas)
|
||||||
|
.png(canvas),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
91
src/ops/area.rs
Normal file
91
src/ops/area.rs
Normal file
|
|
@ -0,0 +1,91 @@
|
||||||
|
use crate::{Canvas, ImgSyn, ImgSynPixFn, Pixel};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct ImgSynMapArea<Prev: ImgSyn, C: ImgSynPixFn<bool>, F: ImgSynPixFn<Pixel>>(
|
||||||
|
pub(crate) Prev,
|
||||||
|
pub(crate) C,
|
||||||
|
pub(crate) F,
|
||||||
|
);
|
||||||
|
|
||||||
|
impl<Prev, C, F> ImgSyn for ImgSynMapArea<Prev, C, F>
|
||||||
|
where
|
||||||
|
Prev: ImgSyn,
|
||||||
|
C: ImgSynPixFn<bool>,
|
||||||
|
F: ImgSynPixFn<Pixel>,
|
||||||
|
{
|
||||||
|
fn getpx(&self, meta: Canvas, x: f32, y: f32) -> crate::Pixel {
|
||||||
|
let px = self.0.getpx(meta, x, y);
|
||||||
|
if !self.1.run(meta, x, y, px) {
|
||||||
|
return px;
|
||||||
|
}
|
||||||
|
self.2.run(meta, x, y, px)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct ImgSynArea<Prev: ImgSyn, ImgB: ImgSyn, C: ImgSynPixFn<bool>>(
|
||||||
|
pub(crate) Prev,
|
||||||
|
pub(crate) ImgB,
|
||||||
|
pub(crate) C,
|
||||||
|
);
|
||||||
|
|
||||||
|
impl<Prev, ImgB, C> ImgSyn for ImgSynArea<Prev, ImgB, C>
|
||||||
|
where
|
||||||
|
Prev: ImgSyn,
|
||||||
|
ImgB: ImgSyn,
|
||||||
|
C: ImgSynPixFn<bool>,
|
||||||
|
{
|
||||||
|
fn getpx(&self, meta: Canvas, x: f32, y: f32) -> crate::Pixel {
|
||||||
|
let px = self.0.getpx(meta, x, y);
|
||||||
|
if !self.2.run(meta, x, y, px) {
|
||||||
|
return px;
|
||||||
|
}
|
||||||
|
self.1.getpx(meta, x, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct ImgSynRect<Prev: ImgSyn, ImgB: ImgSyn>(
|
||||||
|
pub(crate) Prev,
|
||||||
|
pub(crate) ImgB,
|
||||||
|
pub(crate) (f32, f32, f32, f32),
|
||||||
|
);
|
||||||
|
|
||||||
|
impl<Prev, ImgB> ImgSyn for ImgSynRect<Prev, ImgB>
|
||||||
|
where
|
||||||
|
Prev: ImgSyn,
|
||||||
|
ImgB: ImgSyn,
|
||||||
|
{
|
||||||
|
fn getpx(&self, meta: Canvas, x: f32, y: f32) -> crate::Pixel {
|
||||||
|
let px = self.0.getpx(meta, x, y);
|
||||||
|
let (startx, starty, sizex, sizey) = self.2;
|
||||||
|
if x < startx || y < starty {
|
||||||
|
return px;
|
||||||
|
}
|
||||||
|
if x - startx >= sizex || y - starty >= sizey {
|
||||||
|
return px;
|
||||||
|
}
|
||||||
|
self.1.getpx(
|
||||||
|
Canvas::new(sizex * meta.width, sizey * meta.height, meta.default_px),
|
||||||
|
(x - startx) / sizex,
|
||||||
|
(y - starty) / sizey,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct ImgSynUnmapRect<Prev: ImgSyn>(pub(crate) Prev, pub(crate) (f32, f32, f32, f32));
|
||||||
|
|
||||||
|
impl<Prev> ImgSyn for ImgSynUnmapRect<Prev>
|
||||||
|
where
|
||||||
|
Prev: ImgSyn,
|
||||||
|
{
|
||||||
|
fn getpx(&self, meta: Canvas, x: f32, y: f32) -> crate::Pixel {
|
||||||
|
let (startx, starty, sizex, sizey) = self.1;
|
||||||
|
self.0.getpx(
|
||||||
|
Canvas::new(meta.width / sizex, meta.height / sizey, meta.default_px),
|
||||||
|
x * sizex + startx,
|
||||||
|
y * sizey + starty,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
29
src/ops/axial.rs
Normal file
29
src/ops/axial.rs
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
use crate::{
|
||||||
|
Canvas, ImgSyn,
|
||||||
|
assist::{axial, polar},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct ImgSynAxial<Prev: ImgSyn>(pub(crate) Prev);
|
||||||
|
|
||||||
|
impl<Prev> ImgSyn for ImgSynAxial<Prev>
|
||||||
|
where
|
||||||
|
Prev: ImgSyn,
|
||||||
|
{
|
||||||
|
fn getpx(&self, meta: Canvas, x: f32, y: f32) -> crate::Pixel {
|
||||||
|
let (x, y) = axial(x, y, meta);
|
||||||
|
self.0.getpx(meta, x, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct ImgSynPolar<Prev: ImgSyn>(pub(crate) Prev);
|
||||||
|
|
||||||
|
impl<Prev> ImgSyn for ImgSynPolar<Prev>
|
||||||
|
where
|
||||||
|
Prev: ImgSyn,
|
||||||
|
{
|
||||||
|
fn getpx(&self, meta: Canvas, x: f32, y: f32) -> crate::Pixel {
|
||||||
|
let (x, y) = polar(x, y, meta);
|
||||||
|
self.0.getpx(meta, x, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
use crate::{ImgSyn, ImgSynPixFn};
|
use crate::{ImgSyn, ImgSynPixFn, Pixel};
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct ImgSynMap<Prev: ImgSyn, F: ImgSynPixFn>(pub(crate) Prev, pub(crate) F);
|
pub struct ImgSynMap<Prev: ImgSyn, F: ImgSynPixFn<Pixel>>(pub(crate) Prev, pub(crate) F);
|
||||||
|
|
||||||
impl<Prev: ImgSyn, F: ImgSynPixFn> ImgSyn for ImgSynMap<Prev, F> {
|
impl<Prev: ImgSyn, F: ImgSynPixFn<Pixel>> ImgSyn for ImgSynMap<Prev, F> {
|
||||||
fn getpx(&self, meta: crate::Canvas, x: f32, y: f32) -> crate::Pixel {
|
fn getpx(&self, meta: crate::Canvas, x: f32, y: f32) -> crate::Pixel {
|
||||||
self.1.run(meta, x, y, self.0.getpx(meta, x, y))
|
self.1.run(meta, x, y, self.0.getpx(meta, x, y))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,8 @@
|
||||||
mod map;
|
mod map;
|
||||||
pub use map::*;
|
pub use map::*;
|
||||||
|
mod area;
|
||||||
|
pub use area::*;
|
||||||
|
mod axial;
|
||||||
|
pub use axial::*;
|
||||||
|
mod stretch;
|
||||||
|
pub use stretch::*;
|
||||||
|
|
|
||||||
14
src/ops/stretch.rs
Normal file
14
src/ops/stretch.rs
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
use crate::ImgSyn;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct ImgSynStretched<Prev: ImgSyn>(pub(crate) Prev, pub(crate) (f32, f32));
|
||||||
|
|
||||||
|
impl<Prev: ImgSyn> ImgSyn for ImgSynStretched<Prev> {
|
||||||
|
fn getpx(&self, meta: crate::Canvas, x: f32, y: f32) -> crate::Pixel {
|
||||||
|
self.0.getpx(
|
||||||
|
meta,
|
||||||
|
x / self.1.0 - 0.5 / self.1.0 + 0.5,
|
||||||
|
y / self.1.1 - 0.5 / self.1.1 + 0.5,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
32
src/pixel.rs
32
src/pixel.rs
|
|
@ -1,9 +1,9 @@
|
||||||
#[derive(Clone, Copy, PartialEq)]
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
pub struct Pixel {
|
pub struct Pixel {
|
||||||
r: f32,
|
pub r: f32,
|
||||||
g: f32,
|
pub g: f32,
|
||||||
b: f32,
|
pub b: f32,
|
||||||
a: f32,
|
pub a: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pixel {
|
impl Pixel {
|
||||||
|
|
@ -33,6 +33,10 @@ impl Pixel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn of(r: f32, g: f32, b: f32, a: f32) -> Pixel {
|
||||||
|
Self { r, g, b, a }
|
||||||
|
}
|
||||||
|
|
||||||
pub fn with_r(self, x: f32) -> Pixel {
|
pub fn with_r(self, x: f32) -> Pixel {
|
||||||
Self { r: x, ..self }
|
Self { r: x, ..self }
|
||||||
}
|
}
|
||||||
|
|
@ -45,6 +49,17 @@ impl Pixel {
|
||||||
pub fn with_a(self, x: f32) -> Pixel {
|
pub fn with_a(self, x: f32) -> Pixel {
|
||||||
Self { a: x, ..self }
|
Self { a: x, ..self }
|
||||||
}
|
}
|
||||||
|
pub fn with_rgb(self, r: f32, g: f32, b: f32) -> Pixel {
|
||||||
|
Self { r, g, b, ..self }
|
||||||
|
}
|
||||||
|
pub fn with_rgb_of(self, x: f32) -> Pixel {
|
||||||
|
Self {
|
||||||
|
r: x,
|
||||||
|
g: x,
|
||||||
|
b: x,
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn map(self, f: impl FnOnce(Self) -> Pixel) -> Pixel {
|
pub fn map(self, f: impl FnOnce(Self) -> Pixel) -> Pixel {
|
||||||
f(self)
|
f(self)
|
||||||
|
|
@ -66,4 +81,13 @@ impl Pixel {
|
||||||
self.r = f(self.r);
|
self.r = f(self.r);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn rgba255(self) -> [u8; 4] {
|
||||||
|
[
|
||||||
|
(self.r.clamp(0.0, 1.0) * 255.) as u8,
|
||||||
|
(self.g.clamp(0.0, 1.0) * 255.) as u8,
|
||||||
|
(self.b.clamp(0.0, 1.0) * 255.) as u8,
|
||||||
|
(self.a.clamp(0.0, 1.0) * 255.) as u8,
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
29
src/pixfn.rs
29
src/pixfn.rs
|
|
@ -1,28 +1,37 @@
|
||||||
use crate::{Canvas, ImgSynPixFn, Pixel};
|
use crate::{Canvas, ImgSynPixFn, Pixel};
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct PixFnInt<T: (Fn(Canvas, u32, u32, Pixel) -> Pixel) + Copy>(pub T);
|
pub struct PixFnInt<T: Clone + Copy, F: (Fn(Canvas, u32, u32, Pixel) -> T) + Copy>(pub F);
|
||||||
impl<T> ImgSynPixFn for PixFnInt<T>
|
impl<T: Clone + Copy, F> ImgSynPixFn<T> for PixFnInt<T, F>
|
||||||
where
|
where
|
||||||
T: (Fn(Canvas, u32, u32, Pixel) -> Pixel) + Copy,
|
F: (Fn(Canvas, u32, u32, Pixel) -> T) + Copy,
|
||||||
{
|
{
|
||||||
fn run(&self, meta: Canvas, x: f32, y: f32, pixel: Pixel) -> Pixel {
|
fn run(&self, meta: Canvas, x: f32, y: f32, pixel: Pixel) -> T {
|
||||||
self.0(
|
self.0(
|
||||||
meta,
|
meta,
|
||||||
(x * meta.width as f32).round() as u32,
|
(x * meta.width).round() as u32,
|
||||||
(y * meta.height as f32).round() as u32,
|
(y * meta.height).round() as u32,
|
||||||
pixel,
|
pixel,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct PixFnFloat<T: (Fn(Canvas, f32, f32, Pixel) -> Pixel) + Copy>(pub T);
|
pub struct PixFnFloat<T: Clone + Copy, F: (Fn(Canvas, f32, f32, Pixel) -> T) + Copy>(pub F);
|
||||||
impl<T> ImgSynPixFn for PixFnFloat<T>
|
impl<T: Clone + Copy, F> ImgSynPixFn<T> for PixFnFloat<T, F>
|
||||||
where
|
where
|
||||||
T: (Fn(Canvas, f32, f32, Pixel) -> Pixel) + Copy,
|
F: (Fn(Canvas, f32, f32, Pixel) -> T) + Copy,
|
||||||
{
|
{
|
||||||
fn run(&self, meta: Canvas, x: f32, y: f32, pixel: Pixel) -> Pixel {
|
fn run(&self, meta: Canvas, x: f32, y: f32, pixel: Pixel) -> T {
|
||||||
self.0(meta, x, y, pixel)
|
self.0(meta, x, y, pixel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: Clone + Copy, F> ImgSynPixFn<T> for F
|
||||||
|
where
|
||||||
|
F: (Fn(f32, f32, Pixel) -> T) + Copy,
|
||||||
|
{
|
||||||
|
fn run(&self, _meta: Canvas, x: f32, y: f32, px: Pixel) -> T {
|
||||||
|
self(x, y, px)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue