add Image::show

This commit is contained in:
bendn 2023-11-04 17:42:57 +07:00
parent 16fbab6ca2
commit dd74d73361
No known key found for this signature in database
GPG key ID: 0D9D3A2A3B2A93D6
4 changed files with 144 additions and 0 deletions

View file

@ -19,6 +19,10 @@ umath = "0.0.7"
fr = { version = "0.1.1", package = "fer", optional = true }
stackblur-iter = { version = "0.2.0", features = ["simd"], optional = true }
clipline = "0.1.1"
minifb = { version = "0.25.0", default-features = false, features = [
"x11",
"wayland",
], optional = true }
[dev-dependencies]
iai = { git = "https://github.com/bend-n/iai.git" }
@ -53,6 +57,7 @@ scale = ["fr"]
save = ["png"]
text = ["fontdue"]
blur = ["stackblur-iter"]
real-show = ["minifb"]
default = ["save", "scale"]
[profile.release]

View file

@ -47,6 +47,7 @@ impl<T: AsMut<[u8]> + AsRef<[u8]>, const CHANNELS: usize> Image<T, CHANNELS> {
for y in ymin..ymax {
for x in xmin..xmax {
// algorithm from https://web.archive.org/web/20050408192410/http://sw-shader.sourceforge.net/rasterizer.html, but im too dumb to implement the faster ones
// SAFETY: nNaN
if unsafe {
(x1 - x2) * (F::new(y as f32) - y1) + (-(y1 - y2) * (F::new(x as f32) - x1))
> 0.

View file

@ -35,6 +35,17 @@
//! - [`Image::repeated`]
//! - [`Image::overlay`](Overlay), [`Image::overlay_at`](OverlayAt), [`Image::overlay_blended`](BlendingOverlay)
//! - [`Image::blur`]
//!
//! ## feature flags
//!
//! - `scale`: enables the [`scale`] module.
//! - `save`: enables [`Image::save`], via the [`png`](https://crates.io/crates/png) crate.
//! - `text`: enables [`Image::text`], via the [`fontdue`](https://crates.io/crates/fontdue) crate.
//! - `blur`: enables [`Image::blur`], via the [`stackblur`](https://crates.io/crates/stackblur-iter) crate.
//! - `real-show`: [`Image::show`], if the `save` feature is enabled, will, by default, simply open the appropriate image viewing program.
//! if, for some reason, this is inadequate/you dont have a good image viewer, enable the `real-show` feature to make [`Image::show`] open up a window of its own.
//! without the `real-show` feature, [`Image::show`] will save itself to your temp directory, which you may not want.
//! - `default`: \[`save`, `scale`\].
#![feature(
slice_swap_unchecked,
generic_const_exprs,
@ -75,6 +86,8 @@ mod overlay;
pub mod pixels;
#[cfg(feature = "scale")]
pub mod scale;
#[cfg(any(feature = "save", feature = "real-show"))]
mod show;
pub use cloner::ImageCloner;
pub use overlay::{BlendingOverlay, ClonerOverlay, ClonerOverlayAt, Overlay, OverlayAt};
pub use r#dyn::DynImage;

125
src/show.rs Normal file
View file

@ -0,0 +1,125 @@
use crate::Image;
#[cfg(feature = "real-show")]
mod real {
use crate::Image;
use minifb::{Key, Window};
pub fn show(i: Image<&[u32], 1>) {
let mut win = Window::new(
"show",
i.width() as usize,
i.height() as usize,
Default::default(),
)
.unwrap();
win.limit_update_rate(Some(std::time::Duration::from_millis(100)));
while win.is_open() && !win.is_key_down(Key::Q) && !win.is_key_down(Key::Escape) {
win.update_with_buffer(&i.buffer, i.width() as usize, i.height() as usize)
.expect("window update fail");
}
}
}
#[cfg(not(feature = "real-show"))]
mod fake {
use std::process::{Command, Stdio};
macro_rules! c {
($p:literal) => {
std::process::Command::new($p)
};
($p:literal $($args:expr)+) => {
std::process::Command::new($p).args([$($args,)+])
}
}
pub(crate) use c;
pub fn has(c: &'static str) -> bool {
complete(c!("which").arg(c))
}
pub fn complete(c: &mut Command) -> bool {
c.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
.expect("ok")
.success()
}
macro_rules! show {
($me:expr) => {
let file = std::env::temp_dir().join("viewing.png");
$me.save(&file);
#[cfg(target_family = "windows")]
assert!(fake::complete(fake::c!("start" "%Temp%/viewing.png")), "command should complete successfully.");
#[cfg(target_family = "unix")]
assert!(
if fake::has("feh") { fake::complete(fake::c!("feh" file))
} else if fake::has("xdg-open") { fake::complete(fake::c!("xdg-open" file))
} else if fake::has("gio") { fake::complete(fake::c!("gio" file))
} else if fake::has("gnome-open") { fake::complete(fake::c!("gnome-open" file))
} else if fake::has("kde-open") { fake::complete(fake::c!("kde-open" file))
} else if fake::has("open") { fake::complete(fake::c!("open" file))
} else { panic!("no image viewer found, please enable the `real-show` feature.") },
"command should complete successfully.");
};
}
pub(crate) use show;
}
fn r(i: &Image<Box<[u32]>, 1>) -> Image<&[u32], 1> {
// SAFETY: ctor
unsafe { Image::new(i.width, i.height, &*i.buffer) }
}
macro_rules! show {
($buf:ty) => {
show!($buf, 1);
show!($buf, 2);
show!($buf, 3);
show!($buf, 4);
};
($buf:ty, $n:literal) => {
impl Image<$buf, $n> {
/// Open a window showing this image.
/// Blocks until the window finishes.
///
/// This is like [`dbg!`] for images.
///
/// # Panics
///
/// if the window is un creatable
pub fn show(self) -> Self {
#[cfg(feature = "real-show")]
real::show(r(&self.as_ref().into()));
#[cfg(not(feature = "real-show"))]
fake::show!(self);
self
}
}
};
}
show!(Vec<u8>);
show!(Box<[u8]>);
show!(&[u8]);
impl Image<Box<[u32]>, 1> {
/// Open a window showing this image.
/// Blocks until the window finishes.
///
/// This is like [`dbg!`] for images.
///
/// # Panics
///
/// if the window is un creatable
pub fn show(self) -> Self {
#[cfg(feature = "real-show")]
real::show(r(&self));
#[cfg(not(feature = "real-show"))]
fake::show!(Image::<Box<[u8]>, 4>::from(r(&self)));
self
}
}