some small changes

This commit is contained in:
bendn 2024-05-06 11:11:51 +07:00
parent 3a1b6a3435
commit 0dffa5bc99
4 changed files with 90 additions and 31 deletions

View file

@ -1,6 +1,6 @@
[package] [package]
name = "fimg" name = "fimg"
version = "0.4.41" version = "0.4.42"
authors = ["bend-n <bend.n@outlook.com>"] authors = ["bend-n <bend.n@outlook.com>"]
license = "MIT" license = "MIT"
edition = "2021" edition = "2021"
@ -14,19 +14,19 @@ categories = ["multimedia::images", "graphics"]
mattr = "0.0.2" mattr = "0.0.2"
png = { version = "0.17", features = ["unstable"], optional = true } png = { version = "0.17", features = ["unstable"], optional = true }
fontdue = { version = "0.7.3", optional = true } fontdue = { version = "0.7.3", optional = true }
vecto = "0.1.0" vecto = "0.1.1"
umath = "0.0.7" umath = "0.0.7"
fr = { version = "0.1.1", package = "fer", optional = true } fr = { version = "0.1.1", package = "fer", optional = true }
slur = { version = "0.1.0", optional = true } slur = { version = "0.1.0", optional = true }
clipline = "0.1.1" clipline = "0.1.2"
minifb = { version = "0.25.0", default-features = false, features = [ minifb = { version = "0.25.0", default-features = false, features = [
"x11", "x11",
"wayland", "wayland",
], optional = true } ], optional = true }
wgpu = { version = "0.19.1", default-features = false, optional = true } wgpu = { version = "0.19.1", default-features = false, optional = true }
atools = "0.1.0" atools = "0.1.4"
qwant = { version = "1.0.0", optional = true } qwant = { version = "1.0.0", optional = true }
libc = "0.2.153" libc = "0.2.154"
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
windows = { version = "0.53.0", features = [ windows = { version = "0.53.0", features = [

View file

@ -78,6 +78,10 @@ impl<T: Copy, const C: usize> Builder<Box<[T]>, C> {
/// seals the [`Buffer`] trait /// seals the [`Buffer`] trait
mod buf { mod buf {
/// A valid buffer for use in the builder /// A valid buffer for use in the builder
#[diagnostic::on_unimplemented(
message = "this type is not a buffer",
note = "if you think this type is a buffer, please open an issue.\nYou can manually circumvent this warning via Image::new."
)]
pub trait Buffer { pub trait Buffer {
#[doc(hidden)] #[doc(hidden)]
fn len(&self) -> usize; fn len(&self) -> usize;

View file

@ -56,10 +56,8 @@
slice_swap_unchecked, slice_swap_unchecked,
generic_const_exprs, generic_const_exprs,
iter_array_chunks, iter_array_chunks,
split_at_checked,
core_intrinsics, core_intrinsics,
slice_as_chunks, slice_as_chunks,
unchecked_math,
slice_flatten, slice_flatten,
rustc_private, rustc_private,
portable_simd, portable_simd,
@ -320,6 +318,12 @@ impl<T, const CHANNELS: usize> Image<T, CHANNELS> {
// SAFETY: we dont change anything, why check // SAFETY: we dont change anything, why check
unsafe { Image::new(self.width, self.height, f(self.buffer)) } unsafe { Image::new(self.width, self.height, f(self.buffer)) }
} }
/// # Safety
/// buffer size must be the same.
unsafe fn map_into<U: From<T>, const N: usize>(self) -> Image<U, N> {
unsafe { self.mapped(Into::into) }
}
} }
impl<const CHANNELS: usize, T: Clone> Image<&[T], CHANNELS> { impl<const CHANNELS: usize, T: Clone> Image<&[T], CHANNELS> {
@ -379,7 +383,7 @@ impl<const CHANNELS: usize, const N: usize> Image<[u8; N], CHANNELS> {
/// Box this array image. /// Box this array image.
pub fn boxed(self) -> Image<Box<[u8]>, CHANNELS> { pub fn boxed(self) -> Image<Box<[u8]>, CHANNELS> {
// SAFETY: size not changed // SAFETY: size not changed
unsafe { self.mapped(|b| b.into()) } unsafe { self.map_into() }
} }
} }
@ -387,7 +391,7 @@ impl<const CHANNELS: usize> Image<&[u8], CHANNELS> {
/// Box this image. /// Box this image.
pub fn boxed(self) -> Image<Box<[u8]>, CHANNELS> { pub fn boxed(self) -> Image<Box<[u8]>, CHANNELS> {
// SAFETY: size not changed // SAFETY: size not changed
unsafe { self.mapped(|b| b.into()) } unsafe { self.map_into() }
} }
} }
@ -395,7 +399,15 @@ impl<const CHANNELS: usize> Image<Vec<u8>, CHANNELS> {
/// Box this owned image. /// Box this owned image.
pub fn boxed(self) -> Image<Box<[u8]>, CHANNELS> { pub fn boxed(self) -> Image<Box<[u8]>, CHANNELS> {
// SAFETY: ctor // SAFETY: ctor
unsafe { self.mapped(|b| b.into()) } unsafe { self.map_into() }
}
}
impl<const CHANNELS: usize> Image<Box<[u8]>, CHANNELS> {
/// Unbox this vec image.
pub fn unbox(self) -> Image<Vec<u8>, CHANNELS> {
// SAFETY: ctor
unsafe { self.map_into() }
} }
} }
@ -624,6 +636,16 @@ pub trait WritePng {
fn write(&self, f: &mut impl std::io::Write) -> std::io::Result<()>; fn write(&self, f: &mut impl std::io::Write) -> std::io::Result<()>;
} }
/// Read png.
#[cfg(feature = "save")]
pub trait ReadPng
where
Self: Sized,
{
/// Read a png into an image.
fn read(f: &mut impl std::io::Read) -> std::io::Result<Self>;
}
/// helper macro for defining the save() method. /// helper macro for defining the save() method.
macro_rules! save { macro_rules! save {
($channels:literal == $clr:ident ($clrhuman:literal)) => { ($channels:literal == $clr:ident ($clrhuman:literal)) => {
@ -663,34 +685,67 @@ macro_rules! save {
}; };
} }
impl<const CHANNELS: usize> Image<Vec<u8>, CHANNELS> { macro_rules! read {
($n:literal) => {
#[cfg(feature = "save")]
impl ReadPng for Image<Box<[u8]>, $n> {
/// Open a PNG image
fn read(f: &mut impl std::io::Read) -> std::io::Result<Self> {
use png::Transformations as T;
let mut dec = png::Decoder::new(f);
match $n {
1 | 3 => dec.set_transformations(T::STRIP_16 | T::EXPAND),
2 | 4 => dec.set_transformations(T::STRIP_16 | T::ALPHA), // alpha implies expand
_ => (),
}
let mut reader = dec.read_info()?;
let mut buf = vec![0; reader.output_buffer_size()].into_boxed_slice();
let info = reader.next_frame(&mut buf)?;
use png::ColorType::*;
macro_rules! n {
($x:literal) => {
Image::<_, $x>::build(info.width, info.height)
.buf(buf)
.into()
};
}
Ok(match info.color_type {
Indexed => unreachable!(), // see EXPAND
Grayscale => n![1],
GrayscaleAlpha => n![2],
Rgb => n![3],
Rgba => n![4],
})
}
}
};
}
impl<const CHANNELS: usize> Image<Vec<u8>, CHANNELS>
where
[(); { (CHANNELS <= 4) as usize } - 1]:,
{
#[cfg(feature = "save")] #[cfg(feature = "save")]
/// Open a PNG image /// Open a PNG image
pub fn open(f: impl AsRef<std::path::Path>) -> Self { pub fn open(f: impl AsRef<std::path::Path>) -> Self {
use png::Transformations as T;
let p = std::fs::File::open(f).unwrap(); let p = std::fs::File::open(f).unwrap();
let r = std::io::BufReader::new(p); let r = &mut std::io::BufReader::new(p);
let mut dec = png::Decoder::new(r); use core::intrinsics::transmute_unchecked as t;
match CHANNELS { // SAFETY: ... this is idiotic.
1 | 3 => dec.set_transformations(T::STRIP_16 | T::EXPAND), unsafe {
2 | 4 => dec.set_transformations(T::STRIP_16 | T::ALPHA), match CHANNELS {
_ => (), 1 => t(Image::<Box<_>, 1>::read(r).unwrap().unbox()),
} 2 => t(Image::<Box<_>, 2>::read(r).unwrap().unbox()),
let mut reader = dec.read_info().unwrap(); 3 => t(Image::<Box<_>, 3>::read(r).unwrap().unbox()),
let mut buf = vec![0; reader.output_buffer_size()]; 4 => t(Image::<Box<_>, 4>::read(r).unwrap().unbox()),
let info = reader.next_frame(&mut buf).unwrap(); _ => unreachable!(),
use png::ColorType::*;
match info.color_type {
Indexed | Grayscale => {
assert_eq!(CHANNELS, 1, "indexed | grayscale requires one channel")
} }
Rgb => assert_eq!(CHANNELS, 3, "rgb requires three channels"),
Rgba => assert_eq!(CHANNELS, 4, "rgba requires four channels"),
GrayscaleAlpha => assert_eq!(CHANNELS, 2, "ya requires two channels"),
} }
Self::build(info.width, info.height).buf(buf)
} }
} }
read!(1);
read!(2);
read!(3);
read!(4);
save!(3 == Rgb("RGB")); save!(3 == Rgb("RGB"));
save!(4 == Rgba("RGBA")); save!(4 == Rgba("RGBA"));