mirror of
https://github.com/bend-n/fimg.git
synced 2024-12-22 02:28:19 -06:00
dyn
This commit is contained in:
parent
bb33b6cb10
commit
3e01a15713
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "fimg"
|
name = "fimg"
|
||||||
version = "0.4.21"
|
version = "0.4.22"
|
||||||
authors = ["bend-n <bend.n@outlook.com>"]
|
authors = ["bend-n <bend.n@outlook.com>"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
64
src/convert.rs
Normal file
64
src/convert.rs
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
//! define From's for images.
|
||||||
|
//! these conversions are defined by [`PFrom`].
|
||||||
|
use crate::{pixels::convert::PFrom, Image};
|
||||||
|
|
||||||
|
fn map<const A: usize, const B: usize>(image: Image<&[u8], A>) -> Image<Box<[u8]>, B>
|
||||||
|
where
|
||||||
|
[u8; B]: PFrom<A>,
|
||||||
|
{
|
||||||
|
let buffer = image
|
||||||
|
.chunked()
|
||||||
|
.copied()
|
||||||
|
.flat_map(<[u8; B] as PFrom<A>>::pfrom)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.into();
|
||||||
|
// SAFETY: ctor
|
||||||
|
unsafe { Image::new(image.width, image.height, buffer) }
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! convert {
|
||||||
|
($a:literal => $b:literal) => {
|
||||||
|
impl From<Image<&[u8], $b>> for Image<Box<[u8]>, $a> {
|
||||||
|
fn from(value: Image<&[u8], $b>) -> Self {
|
||||||
|
map(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! cv {
|
||||||
|
[$($n:literal),+] => {
|
||||||
|
$(convert!($n => 1);
|
||||||
|
convert!($n => 2);
|
||||||
|
convert!($n => 3);
|
||||||
|
convert!($n => 4);)+
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
cv![1, 2, 3, 4];
|
||||||
|
|
||||||
|
macro_rules! boxconv {
|
||||||
|
($a:literal => $b: literal) => {
|
||||||
|
impl From<Image<Box<[u8]>, $b>> for Image<Box<[u8]>, $a> {
|
||||||
|
fn from(value: Image<Box<[u8]>, $b>) -> Self {
|
||||||
|
value.as_ref().into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
boxconv!(1 => 2);
|
||||||
|
boxconv!(1 => 3);
|
||||||
|
boxconv!(1 => 4);
|
||||||
|
|
||||||
|
boxconv!(2 => 1);
|
||||||
|
boxconv!(2 => 3);
|
||||||
|
boxconv!(2 => 4);
|
||||||
|
|
||||||
|
boxconv!(3 => 1);
|
||||||
|
boxconv!(3 => 2);
|
||||||
|
boxconv!(3 => 4);
|
||||||
|
|
||||||
|
boxconv!(4 => 1);
|
||||||
|
boxconv!(4 => 2);
|
||||||
|
boxconv!(4 => 3);
|
38
src/dyn/affine.rs
Normal file
38
src/dyn/affine.rs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
use super::{e, DynImage};
|
||||||
|
|
||||||
|
impl<T: AsMut<[u8]> + AsRef<[u8]>> DynImage<T> {
|
||||||
|
/// Rotate this image 90 degrees clockwise.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// UB if this image is not square
|
||||||
|
pub unsafe fn rot_90(&mut self) {
|
||||||
|
// SAFETY: caller guarantees
|
||||||
|
unsafe { e!(self, |i| i.rot_90()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Rotate this image 180 degrees clockwise.
|
||||||
|
pub fn rot_180(&mut self) {
|
||||||
|
e!(self, |i| i.rot_180())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Rotate this image 270 degrees clockwise.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// UB if this image is not square
|
||||||
|
pub unsafe fn rot_270(&mut self) {
|
||||||
|
// SAFETY: caller guarantees
|
||||||
|
unsafe { e!(self, |i| i.rot_270()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Flip this image horizontally.
|
||||||
|
pub fn flip_h(&mut self) {
|
||||||
|
e!(self, |i| i.flip_h())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Flip this image vertically.
|
||||||
|
pub fn flip_v(&mut self) {
|
||||||
|
e!(self, |i| i.flip_v())
|
||||||
|
}
|
||||||
|
}
|
110
src/dyn/convert.rs
Normal file
110
src/dyn/convert.rs
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
#![allow(clippy::useless_conversion)]
|
||||||
|
use super::{e, DynImage, Image};
|
||||||
|
|
||||||
|
impl From<DynImage<Box<[u8]>>> for Image<Box<[u8]>, 1> {
|
||||||
|
fn from(value: DynImage<Box<[u8]>>) -> Self {
|
||||||
|
e!(value, |i| i.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<DynImage<Box<[u8]>>> for Image<Box<[u8]>, 2> {
|
||||||
|
fn from(value: DynImage<Box<[u8]>>) -> Self {
|
||||||
|
e!(value, |i| i.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<DynImage<Box<[u8]>>> for Image<Box<[u8]>, 3> {
|
||||||
|
fn from(value: DynImage<Box<[u8]>>) -> Self {
|
||||||
|
e!(value, |i| i.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<DynImage<Box<[u8]>>> for Image<Box<[u8]>, 4> {
|
||||||
|
fn from(value: DynImage<Box<[u8]>>) -> Self {
|
||||||
|
e!(value, |i| i.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> DynImage<T> {
|
||||||
|
/// Gets out the Y image, if its there, else returning self.
|
||||||
|
///
|
||||||
|
/// If you want to convert, see [`DynImage::to_y`].
|
||||||
|
pub fn get_y(self) -> Result<Image<T, 1>, Self> {
|
||||||
|
match self {
|
||||||
|
Self::Y(i) => Ok(i),
|
||||||
|
_ => Err(self),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets out the YA image, if its there, else returning self.
|
||||||
|
///
|
||||||
|
/// If you want to convert, see [`DynImage::to_ya`].
|
||||||
|
pub fn get_ya(self) -> Result<Image<T, 2>, Self> {
|
||||||
|
match self {
|
||||||
|
Self::Ya(i) => Ok(i),
|
||||||
|
_ => Err(self),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets out the RGB image, if its there, else returning self.
|
||||||
|
///
|
||||||
|
/// If you want to convert, see [`DynImage::to_rgb`].
|
||||||
|
pub fn get_rgb(self) -> Result<Image<T, 3>, Self> {
|
||||||
|
match self {
|
||||||
|
Self::Rgb(i) => Ok(i),
|
||||||
|
_ => Err(self),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets out the RGBA image, if its there, else returning self.
|
||||||
|
///
|
||||||
|
/// If you want to convert, see [`DynImage::to_rgba`].
|
||||||
|
pub fn get_rgba(self) -> Result<Image<T, 4>, Self> {
|
||||||
|
match self {
|
||||||
|
Self::Rgba(i) => Ok(i),
|
||||||
|
_ => Err(self),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DynImage<Box<[u8]>> {
|
||||||
|
/// Convert this dyn image into a Y image.
|
||||||
|
pub fn to_y(self) -> Image<Box<[u8]>, 1> {
|
||||||
|
self.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert this dyn image into a YA image.
|
||||||
|
pub fn to_ya(self) -> Image<Box<[u8]>, 2> {
|
||||||
|
self.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert this dyn image into a RGB image.
|
||||||
|
pub fn to_rgb(self) -> Image<Box<[u8]>, 3> {
|
||||||
|
self.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert this dyn image into a RGBA image.
|
||||||
|
pub fn to_rgba(self) -> Image<Box<[u8]>, 4> {
|
||||||
|
self.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: AsRef<[u8]>> DynImage<T> {
|
||||||
|
/// Produce a Y image from this dyn image.
|
||||||
|
pub fn y(&self) -> Image<Box<[u8]>, 1> {
|
||||||
|
e!(self, |i| i.as_ref().into())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Produce a YA image from this dyn image.
|
||||||
|
pub fn ya(&self) -> Image<Box<[u8]>, 2> {
|
||||||
|
e!(self, |i| i.as_ref().into())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Produce a RGB image from this dyn image.
|
||||||
|
pub fn rgb(&self) -> Image<Box<[u8]>, 3> {
|
||||||
|
e!(self, |i| i.as_ref().into())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Produce a RGBA image from this dyn image.
|
||||||
|
pub fn rgba(&self) -> Image<Box<[u8]>, 4> {
|
||||||
|
e!(self, |i| i.as_ref().into())
|
||||||
|
}
|
||||||
|
}
|
121
src/dyn/mod.rs
Normal file
121
src/dyn/mod.rs
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
use crate::Image;
|
||||||
|
mod affine;
|
||||||
|
mod convert;
|
||||||
|
#[cfg(feature = "scale")]
|
||||||
|
mod scale;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
/// Dynamic image.
|
||||||
|
/// Can be any of {`Y8`, `YA8`, `RGB8`, `RGB16`}.
|
||||||
|
pub enum DynImage<T> {
|
||||||
|
/// Y image
|
||||||
|
Y(Image<T, 1>),
|
||||||
|
/// YA image
|
||||||
|
Ya(Image<T, 2>),
|
||||||
|
/// RGB image
|
||||||
|
Rgb(Image<T, 3>),
|
||||||
|
/// RGBA image
|
||||||
|
Rgba(Image<T, 4>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Copy for DynImage<&[u8]> {}
|
||||||
|
|
||||||
|
macro_rules! e {
|
||||||
|
($dyn:expr => |$image: pat_param| $do:expr) => {
|
||||||
|
match $dyn {
|
||||||
|
DynImage::Y($image) => DynImage::Y($do),
|
||||||
|
DynImage::Ya($image) => DynImage::Ya($do),
|
||||||
|
DynImage::Rgb($image) => DynImage::Rgb($do),
|
||||||
|
DynImage::Rgba($image) => DynImage::Rgba($do),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
($dyn:expr, |$image: pat_param| $do:expr) => {
|
||||||
|
match $dyn {
|
||||||
|
DynImage::Y($image) => $do,
|
||||||
|
DynImage::Ya($image) => $do,
|
||||||
|
DynImage::Rgb($image) => $do,
|
||||||
|
DynImage::Rgba($image) => $do,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
use e;
|
||||||
|
|
||||||
|
impl<T> DynImage<T> {
|
||||||
|
/// Get the width of this image.
|
||||||
|
pub const fn width(&self) -> u32 {
|
||||||
|
e!(self, |i| i.width())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the height of this image.
|
||||||
|
pub const fn height(&self) -> u32 {
|
||||||
|
e!(self, |i| i.height())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the image buffer.
|
||||||
|
pub const fn buffer(&self) -> &T {
|
||||||
|
e!(self, |i| i.buffer())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Take the image buffer.
|
||||||
|
pub fn take_buffer(self) -> T {
|
||||||
|
e!(self, |i| i.take_buffer())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: AsRef<[u8]>> DynImage<T> {
|
||||||
|
/// Reference this image.
|
||||||
|
pub fn as_ref(&self) -> DynImage<&[u8]> {
|
||||||
|
e!(self => |i| i.as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Bytes of this image.
|
||||||
|
pub fn bytes(&self) -> &[u8] {
|
||||||
|
e!(self, |i| i.bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DynImage<Box<[u8]>> {
|
||||||
|
#[cfg(feature = "save")]
|
||||||
|
/// Open a PNG image
|
||||||
|
pub fn open(f: impl AsRef<std::path::Path>) -> Self {
|
||||||
|
use png::Transformations as T;
|
||||||
|
let p = std::fs::File::open(f).unwrap();
|
||||||
|
let r = std::io::BufReader::new(p);
|
||||||
|
let mut dec = png::Decoder::new(r);
|
||||||
|
dec.set_transformations(T::STRIP_16 | T::EXPAND);
|
||||||
|
let mut reader = dec.read_info().unwrap();
|
||||||
|
let mut buf = vec![0; reader.output_buffer_size()].into_boxed_slice();
|
||||||
|
let info = reader.next_frame(&mut buf).unwrap();
|
||||||
|
use png::ColorType::*;
|
||||||
|
match info.color_type {
|
||||||
|
Indexed | Grayscale => Self::Y(Image::build(info.width, info.height).buf(buf)),
|
||||||
|
Rgb => Self::Rgb(Image::build(info.width, info.height).buf(buf)),
|
||||||
|
Rgba => Self::Rgba(Image::build(info.width, info.height).buf(buf)),
|
||||||
|
GrayscaleAlpha => Self::Ya(Image::build(info.width, info.height).buf(buf)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "save")]
|
||||||
|
/// Save this image to a PNG.
|
||||||
|
pub fn save(&self, f: impl AsRef<std::path::Path>) {
|
||||||
|
let p = std::fs::File::create(f).unwrap();
|
||||||
|
let w = &mut std::io::BufWriter::new(p);
|
||||||
|
let mut enc = png::Encoder::new(w, self.width(), self.height());
|
||||||
|
enc.set_depth(png::BitDepth::Eight);
|
||||||
|
enc.set_color(match self {
|
||||||
|
Self::Y(_) => png::ColorType::Grayscale,
|
||||||
|
Self::Ya(_) => png::ColorType::GrayscaleAlpha,
|
||||||
|
Self::Rgb(_) => png::ColorType::Rgb,
|
||||||
|
Self::Rgba(_) => png::ColorType::Rgba,
|
||||||
|
});
|
||||||
|
enc.set_source_gamma(png::ScaledFloat::new(1.0 / 2.2));
|
||||||
|
enc.set_source_chromaticities(png::SourceChromaticities::new(
|
||||||
|
(0.31270, 0.32900),
|
||||||
|
(0.64000, 0.33000),
|
||||||
|
(0.30000, 0.60000),
|
||||||
|
(0.15000, 0.06000),
|
||||||
|
));
|
||||||
|
let mut writer = enc.write_header().unwrap();
|
||||||
|
writer.write_image_data(self.bytes()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
10
src/dyn/scale.rs
Normal file
10
src/dyn/scale.rs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
use crate::scale::traits::ScalingAlgorithm;
|
||||||
|
|
||||||
|
use super::{e, DynImage};
|
||||||
|
|
||||||
|
impl<T: AsMut<[u8]> + AsRef<[u8]>> DynImage<T> {
|
||||||
|
/// Scale this image with a given scaling algorithm.
|
||||||
|
pub fn scale<A: ScalingAlgorithm>(&mut self, width: u32, height: u32) -> DynImage<Box<[u8]>> {
|
||||||
|
e!(self => |i| i.scale::<A>(width, height))
|
||||||
|
}
|
||||||
|
}
|
72
src/lib.rs
72
src/lib.rs
|
@ -1,6 +1,39 @@
|
||||||
//! # fimg
|
//! # fimg
|
||||||
//!
|
//!
|
||||||
//! Provides fast image operations, such as rotation, flipping, and overlaying.
|
//! Provides fast image operations, such as rotation, flipping, and overlaying.
|
||||||
|
//!
|
||||||
|
//! ## Organization
|
||||||
|
//!
|
||||||
|
//! Image types:
|
||||||
|
//!
|
||||||
|
//! - [`Image`]: the main image type.
|
||||||
|
//! - [`DynImage`]: This is the image type you use when, say, loading a png. You should immediately convert this into a
|
||||||
|
//! - [`ImageCloner`]: this is... a [`Image`], but about to be cloned. It just allows some simple out-of-place optimizations, that `.clone().op()` dont allow. (produce with [`Image::cloner`])
|
||||||
|
//!
|
||||||
|
//! ### Operations
|
||||||
|
//!
|
||||||
|
//! Affine:
|
||||||
|
//! - [`Image::rot_90`]
|
||||||
|
//! - [`Image::rot_180`]
|
||||||
|
//! - [`Image::rot_270`]
|
||||||
|
//! - [`Image::flip_h`]
|
||||||
|
//! - [`Image::flip_v`]
|
||||||
|
//!
|
||||||
|
//! Drawing:
|
||||||
|
//! - [`Image::box`], [`Image::filled_box`], [`Image::stroked_box`]
|
||||||
|
//! - [`Image::circle`], [`Image::border_circle`]
|
||||||
|
//! - [`Image::line`], [`Image::thick_line`]
|
||||||
|
//! - [`Image::points`]
|
||||||
|
//! - [`Image::quad`]
|
||||||
|
//! - [`Image::poly`], [`Image::border_poly`]
|
||||||
|
//! - [`Image::tri`]
|
||||||
|
//! - [`Image::text`]
|
||||||
|
//!
|
||||||
|
//! Scaling: [`Image::scale`]
|
||||||
|
//!
|
||||||
|
//! Misc image ops:
|
||||||
|
//! - [`Image::repeated`]
|
||||||
|
//! - [`Image::overlay`](Overlay), [`Image::overlay_at`](OverlayAt), [`Image::overlay_blended`](BlendingOverlay)
|
||||||
#![feature(
|
#![feature(
|
||||||
slice_swap_unchecked,
|
slice_swap_unchecked,
|
||||||
generic_const_exprs,
|
generic_const_exprs,
|
||||||
|
@ -27,16 +60,21 @@
|
||||||
use std::{num::NonZeroU32, slice::SliceIndex};
|
use std::{num::NonZeroU32, slice::SliceIndex};
|
||||||
|
|
||||||
mod affine;
|
mod affine;
|
||||||
|
#[doc(hidden)]
|
||||||
pub mod builder;
|
pub mod builder;
|
||||||
|
#[doc(hidden)]
|
||||||
pub mod cloner;
|
pub mod cloner;
|
||||||
|
mod convert;
|
||||||
mod drawing;
|
mod drawing;
|
||||||
|
mod r#dyn;
|
||||||
pub(crate) mod math;
|
pub(crate) mod math;
|
||||||
mod overlay;
|
mod overlay;
|
||||||
pub mod pixels;
|
pub mod pixels;
|
||||||
#[cfg(feature = "scale")]
|
#[cfg(feature = "scale")]
|
||||||
pub mod scale;
|
pub mod scale;
|
||||||
use cloner::ImageCloner;
|
pub use cloner::ImageCloner;
|
||||||
pub use overlay::{BlendingOverlay, ClonerOverlay, ClonerOverlayAt, Overlay, OverlayAt};
|
pub use overlay::{BlendingOverlay, ClonerOverlay, ClonerOverlayAt, Overlay, OverlayAt};
|
||||||
|
pub use r#dyn::DynImage;
|
||||||
|
|
||||||
/// like assert!(), but causes undefined behaviour at runtime when the condition is not met.
|
/// like assert!(), but causes undefined behaviour at runtime when the condition is not met.
|
||||||
///
|
///
|
||||||
|
@ -145,14 +183,14 @@ impl<T: Clone, const CHANNELS: usize> Clone for Image<T, CHANNELS> {
|
||||||
impl<T, const CHANNELS: usize> Image<T, CHANNELS> {
|
impl<T, const CHANNELS: usize> Image<T, CHANNELS> {
|
||||||
#[inline]
|
#[inline]
|
||||||
/// get the height as a [`u32`]
|
/// get the height as a [`u32`]
|
||||||
pub fn height(&self) -> u32 {
|
pub const fn height(&self) -> u32 {
|
||||||
self.height.into()
|
self.height.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
/// get the width as a [`u32`]
|
/// get the width as a [`u32`]
|
||||||
pub fn width(&self) -> u32 {
|
pub const fn width(&self) -> u32 {
|
||||||
self.width.into()
|
self.width.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -299,7 +337,12 @@ impl<T: AsRef<[u8]>, const CHANNELS: usize> Image<T, CHANNELS> {
|
||||||
/// The size of the underlying buffer.
|
/// The size of the underlying buffer.
|
||||||
#[allow(clippy::len_without_is_empty)]
|
#[allow(clippy::len_without_is_empty)]
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.buffer.as_ref().len()
|
self.bytes().len()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Bytes of this image.
|
||||||
|
pub fn bytes(&self) -> &[u8] {
|
||||||
|
self.buffer.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
|
@ -342,7 +385,7 @@ impl<T: AsRef<[u8]>, const CHANNELS: usize> Image<T, CHANNELS> {
|
||||||
/// Reference this image.
|
/// Reference this image.
|
||||||
pub fn as_ref(&self) -> Image<&[u8], CHANNELS> {
|
pub fn as_ref(&self) -> Image<&[u8], CHANNELS> {
|
||||||
// SAFETY: we got constructed okay, parameters must be valid
|
// SAFETY: we got constructed okay, parameters must be valid
|
||||||
unsafe { Image::new(self.width, self.height, self.buffer.as_ref()) }
|
unsafe { Image::new(self.width, self.height, self.bytes()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -352,14 +395,14 @@ impl<T: AsRef<[u8]>, const CHANNELS: usize> Image<T, CHANNELS> {
|
||||||
unsafe { assert_unchecked!(self.len() > CHANNELS) };
|
unsafe { assert_unchecked!(self.len() > CHANNELS) };
|
||||||
// SAFETY: no half pixels!
|
// SAFETY: no half pixels!
|
||||||
unsafe { assert_unchecked!(self.len() % CHANNELS == 0) };
|
unsafe { assert_unchecked!(self.len() % CHANNELS == 0) };
|
||||||
self.buffer.as_ref().array_chunks::<CHANNELS>()
|
self.bytes().array_chunks::<CHANNELS>()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
/// Flatten the chunks of this image into a slice of slices.
|
/// Flatten the chunks of this image into a slice of slices.
|
||||||
pub fn flatten(&self) -> &[[u8; CHANNELS]] {
|
pub fn flatten(&self) -> &[[u8; CHANNELS]] {
|
||||||
// SAFETY: buffer cannot have half pixels
|
// SAFETY: buffer cannot have half pixels
|
||||||
unsafe { self.buffer.as_ref().as_chunks_unchecked::<CHANNELS>() }
|
unsafe { self.bytes().as_chunks_unchecked::<CHANNELS>() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a pixel at (x, y).
|
/// Return a pixel at (x, y).
|
||||||
|
@ -427,7 +470,7 @@ impl<T: AsMut<[u8]> + AsRef<[u8]>, const CHANNELS: usize> Image<T, CHANNELS> {
|
||||||
unsafe fn copy_within(&mut self, src: std::ops::Range<usize>, dest: usize) {
|
unsafe fn copy_within(&mut self, src: std::ops::Range<usize>, dest: usize) {
|
||||||
let std::ops::Range { start, end } = src;
|
let std::ops::Range { start, end } = src;
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
dest <= self.buffer.as_ref().len() - end - start,
|
dest <= self.bytes().len() - end - start,
|
||||||
"dest is out of bounds"
|
"dest is out of bounds"
|
||||||
);
|
);
|
||||||
#[allow(clippy::multiple_unsafe_ops_per_block)]
|
#[allow(clippy::multiple_unsafe_ops_per_block)]
|
||||||
|
@ -516,7 +559,7 @@ macro_rules! save {
|
||||||
(0.15000, 0.06000),
|
(0.15000, 0.06000),
|
||||||
));
|
));
|
||||||
let mut writer = enc.write_header().unwrap();
|
let mut writer = enc.write_header().unwrap();
|
||||||
writer.write_image_data(self.buffer.as_ref()).unwrap();
|
writer.write_image_data(self.bytes()).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -531,13 +574,10 @@ impl<const CHANNELS: usize> Image<Vec<u8>, CHANNELS> {
|
||||||
let r = std::io::BufReader::new(p);
|
let r = std::io::BufReader::new(p);
|
||||||
let mut dec = png::Decoder::new(r);
|
let mut dec = png::Decoder::new(r);
|
||||||
match CHANNELS {
|
match CHANNELS {
|
||||||
1 => dec.set_transformations(T::STRIP_16 | T::EXPAND),
|
1 | 3 => dec.set_transformations(T::STRIP_16 | T::EXPAND),
|
||||||
2 => dec.set_transformations(T::STRIP_16 | T::ALPHA),
|
2 | 4 => dec.set_transformations(T::STRIP_16 | T::ALPHA),
|
||||||
3 => dec.set_transformations(T::STRIP_16 | T::EXPAND),
|
|
||||||
4 => dec.set_transformations(T::STRIP_16 | T::ALPHA),
|
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
dec.set_transformations(png::Transformations::EXPAND);
|
|
||||||
let mut reader = dec.read_info().unwrap();
|
let mut reader = dec.read_info().unwrap();
|
||||||
let mut buf = vec![0; reader.output_buffer_size()];
|
let mut buf = vec![0; reader.output_buffer_size()];
|
||||||
let info = reader.next_frame(&mut buf).unwrap();
|
let info = reader.next_frame(&mut buf).unwrap();
|
||||||
|
|
97
src/pixels/convert.rs
Normal file
97
src/pixels/convert.rs
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
//! provides From's for pixels.
|
||||||
|
|
||||||
|
use super::{Push, Trunc};
|
||||||
|
|
||||||
|
/// Converts a pixel to another pixel.
|
||||||
|
pub trait PFrom<const N: usize> {
|
||||||
|
/// Convert a pixel to this pixel.
|
||||||
|
fn pfrom(f: [u8; N]) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const N: usize> PFrom<N> for [u8; N] {
|
||||||
|
fn pfrom(f: [u8; N]) -> Self {
|
||||||
|
f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Y pixel
|
||||||
|
pub type Y = [u8; 1];
|
||||||
|
impl PFrom<2> for Y {
|
||||||
|
fn pfrom(f: YA) -> Self {
|
||||||
|
f.trunc()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PFrom<3> for Y {
|
||||||
|
fn pfrom([r, g, b]: RGB) -> Self {
|
||||||
|
[((2126 * r as u16 + 7152 * g as u16 + 722 * b as u16) / 10000) as u8]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PFrom<4> for Y {
|
||||||
|
fn pfrom(f: RGBA) -> Self {
|
||||||
|
PFrom::pfrom(f.trunc())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// YA pixel
|
||||||
|
pub type YA = [u8; 2];
|
||||||
|
impl PFrom<1> for YA {
|
||||||
|
fn pfrom(f: Y) -> Self {
|
||||||
|
f.and(255)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PFrom<3> for YA {
|
||||||
|
fn pfrom(f: RGB) -> Self {
|
||||||
|
Y::pfrom(f).and(255)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PFrom<4> for YA {
|
||||||
|
fn pfrom(f: RGBA) -> Self {
|
||||||
|
Y::pfrom(f.trunc()).and(255)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// RGB pixel
|
||||||
|
pub type RGB = [u8; 3];
|
||||||
|
|
||||||
|
impl PFrom<1> for RGB {
|
||||||
|
fn pfrom([y]: Y) -> Self {
|
||||||
|
[y; 3]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PFrom<2> for RGB {
|
||||||
|
fn pfrom([y, _]: YA) -> Self {
|
||||||
|
[y; 3]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PFrom<4> for RGB {
|
||||||
|
fn pfrom(f: RGBA) -> Self {
|
||||||
|
f.trunc()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// RGBA pixel
|
||||||
|
pub type RGBA = [u8; 4];
|
||||||
|
|
||||||
|
impl PFrom<1> for RGBA {
|
||||||
|
fn pfrom([y]: Y) -> Self {
|
||||||
|
[y; 3].and(255)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PFrom<2> for RGBA {
|
||||||
|
fn pfrom([y, a]: YA) -> Self {
|
||||||
|
[y; 3].and(a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PFrom<3> for RGBA {
|
||||||
|
fn pfrom(f: [u8; 3]) -> Self {
|
||||||
|
f.and(255)
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,5 +4,6 @@ pub mod blending;
|
||||||
mod utility;
|
mod utility;
|
||||||
mod wam;
|
mod wam;
|
||||||
pub use blending::Blend;
|
pub use blending::Blend;
|
||||||
pub(crate) use utility::{float, unfloat, Floatify, PMap, Trunc, Unfloatify};
|
pub(crate) use utility::{float, unfloat, Floatify, PMap, Push, Trunc, Unfloatify};
|
||||||
pub(crate) use wam::Wam;
|
pub(crate) use wam::Wam;
|
||||||
|
pub mod convert;
|
||||||
|
|
|
@ -53,7 +53,7 @@ impl<const N: usize, T: Copy, R: Copy> PMap<T, R, N> for [T; N] {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Trunc<T, const N: usize> {
|
pub trait Trunc<T, const N: usize> {
|
||||||
/// it does `a[..a.len() - 1].try_into().unwrap()``.
|
/// it does `a[..a.len() - 1].try_into().unwrap()`.
|
||||||
fn trunc(&self) -> [T; N - 1];
|
fn trunc(&self) -> [T; N - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,6 +63,17 @@ impl<const N: usize, T: Copy> Trunc<T, N> for [T; N] {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait Push<T, const N: usize> {
|
||||||
|
fn and(self, and: T) -> [T; N + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const N: usize, T> Push<T, N> for [T; N] {
|
||||||
|
fn and(self, and: T) -> [T; N + 1] {
|
||||||
|
let mut iter = self.into_iter().chain(std::iter::once(and));
|
||||||
|
std::array::from_fn(|_| iter.next().unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn trunc() {
|
fn trunc() {
|
||||||
let x = [1];
|
let x = [1];
|
||||||
|
@ -70,3 +81,9 @@ fn trunc() {
|
||||||
let x = [1, 2, 3, 4];
|
let x = [1, 2, 3, 4];
|
||||||
assert_eq!(x.trunc(), [1, 2, 3]);
|
assert_eq!(x.trunc(), [1, 2, 3]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn append() {
|
||||||
|
let x = [1];
|
||||||
|
assert_eq!(x.and(5), [1, 5]);
|
||||||
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ pub trait Wam {
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// pls make l = 0..=f32::MAX/2, r = 0..=f32::MAX/2
|
/// pls make l = <code>0..=[f32::MAX]/2</code>, r = <code>0..=[f32::MAX]/2</code>
|
||||||
unsafe fn wam(self, b: Self, l: FF32, r: FF32) -> Self;
|
unsafe fn wam(self, b: Self, l: FF32, r: FF32) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue