optimize repeat

This commit is contained in:
bendn 2024-01-22 21:40:45 +07:00
parent 3085edbed5
commit 1f71a8ab8b
No known key found for this signature in database
GPG key ID: 0D9D3A2A3B2A93D6
3 changed files with 56 additions and 32 deletions

View file

@ -1,6 +1,6 @@
[package] [package]
name = "fimg" name = "fimg"
version = "0.4.31" version = "0.4.32"
authors = ["bend-n <bend.n@outlook.com>"] authors = ["bend-n <bend.n@outlook.com>"]
license = "MIT" license = "MIT"
edition = "2021" edition = "2021"

View file

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2023 bendn Copyright (c) 2024 bendn
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View file

@ -48,6 +48,7 @@
//! without the `real-show` feature, [`Image::show`] will save itself to your temp directory, which you may not want. //! without the `real-show` feature, [`Image::show`] will save itself to your temp directory, which you may not want.
//! - `default`: \[`save`, `scale`\]. //! - `default`: \[`save`, `scale`\].
#![feature( #![feature(
maybe_uninit_write_slice,
slice_swap_unchecked, slice_swap_unchecked,
generic_const_exprs, generic_const_exprs,
slice_as_chunks, slice_as_chunks,
@ -71,7 +72,7 @@
missing_docs missing_docs
)] )]
#![allow(clippy::zero_prefixed_literal, incomplete_features)] #![allow(clippy::zero_prefixed_literal, incomplete_features)]
use std::{num::NonZeroU32, ops::Range}; use std::{mem::MaybeUninit, num::NonZeroU32, ops::Range};
mod affine; mod affine;
#[cfg(feature = "blur")] #[cfg(feature = "blur")]
@ -97,6 +98,28 @@ 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; pub use r#dyn::DynImage;
trait CopyWithinUnchecked {
/// # Safety
///
/// panicless version of [`[T]::copy_within`](`slice::copy_within`), where the slices cant overlap. this uses `memcpy`.
/// your slices must be in bounds.
/// this isnt a public function, so im not going to say exactly what "in bounds" meeans.
unsafe fn copy_within_unchecked(&mut self, src: Range<usize>, dest: usize);
}
impl<T> CopyWithinUnchecked for [T] {
unsafe fn copy_within_unchecked(&mut self, src: Range<usize>, dest: usize) {
let std::ops::Range { start, end } = src;
debug_assert!(dest <= self.len() - end - start, "dest is out of bounds");
#[allow(clippy::multiple_unsafe_ops_per_block)]
// SAFETY: the caller better be good
unsafe {
let ptr = self.as_mut_ptr();
std::ptr::copy_nonoverlapping(ptr.add(start), ptr.add(dest), end - start)
};
}
}
/// 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.
/// ///
/// # Safety /// # Safety
@ -129,7 +152,9 @@ impl Image<&[u8], 3> {
/// ``` /// ```
#[must_use = "function does not modify the original image"] #[must_use = "function does not modify the original image"]
pub unsafe fn repeated(&self, out_width: u32, out_height: u32) -> Image<Vec<u8>, 3> { pub unsafe fn repeated(&self, out_width: u32, out_height: u32) -> Image<Vec<u8>, 3> {
let mut img = Image::<_, 3>::alloc(out_width, out_height); let mut img = Vec::with_capacity(3 * out_width as usize * out_height as usize);
debug_assert!(out_width % self.width() == 0);
debug_assert!(out_height % self.height() == 0);
for y in 0..self.height() { for y in 0..self.height() {
// SAFETY: get one row of pixels // SAFETY: get one row of pixels
let from = unsafe { let from = unsafe {
@ -137,25 +162,43 @@ impl Image<&[u8], 3> {
.get_unchecked(self.at(0, y)..self.at(0, y) + (self.width() as usize * 3)) .get_unchecked(self.at(0, y)..self.at(0, y) + (self.width() as usize * 3))
}; };
debug_assert_eq!(from.len(), self.width() as usize * 3); debug_assert_eq!(from.len(), self.width() as usize * 3);
let first = img.at(0, y)..img.at(self.width(), y); let first =
((y * out_width) as usize * 3)..((y * out_width + self.width()) as usize * 3);
// SAFETY: put it in the output // SAFETY: put it in the output
let to = unsafe { img.buffer.get_unchecked_mut(first.clone()) }; let to = unsafe { img.spare_capacity_mut().get_unchecked_mut(first.clone()) };
// copy it in // copy it in
to.copy_from_slice(from); unsafe { assert_unchecked!(to.len() == from.len()) };
MaybeUninit::write_slice(to, from);
for x in 1..(out_width / self.width()) { for x in 1..(out_width / self.width()) {
let section = img.at(x * self.width(), y); let section = (y * out_width + x * self.width()) as usize * 3;
// SAFETY: copy each row of the image one by one // SAFETY: copy each row of the image one by one
unsafe { img.copy_within(first.clone(), section) }; unsafe {
img.spare_capacity_mut()
.copy_within_unchecked(first.clone(), section)
};
} }
} }
let first_row = 0..img.at(0, self.height()); let first_row = 0..(self.height() * out_width) as usize * 3;
for y in 1..(out_height / self.height()) { for y in 1..(out_height / self.height()) {
let this_row = img.at(0, y * self.height()); let this_row = (y * self.height() * out_width) as usize * 3;
// SAFETY: copy entire blocks of image at a time // SAFETY: copy entire blocks of image at a time
unsafe { img.copy_within(first_row.clone(), this_row) }; unsafe {
img.spare_capacity_mut()
.copy_within_unchecked(first_row.clone(), this_row)
};
}
// SAFETY: we init
unsafe { img.set_len(3 * out_width as usize * out_height as usize) };
// SAFETY: ok
unsafe {
Image::new(
out_width.try_into().unwrap(),
out_height.try_into().unwrap(),
img,
)
} }
img
} }
} }
@ -520,25 +563,6 @@ impl<T: AsMut<[u8]> + AsRef<[u8]>, const CHANNELS: usize> Image<T, CHANNELS> {
unsafe { self.buffer.as_mut().as_chunks_unchecked_mut::<CHANNELS>() } unsafe { self.buffer.as_mut().as_chunks_unchecked_mut::<CHANNELS>() }
} }
/// # Safety
///
/// panicless version of [`[T]::copy_within`](`slice::copy_within`), where the slices cant overlap. this uses `memcpy`.
/// your slices must be in bounds.
/// this isnt a public function, so im not going to say exactly what "in bounds" meeans.
unsafe fn copy_within(&mut self, src: std::ops::Range<usize>, dest: usize) {
let std::ops::Range { start, end } = src;
debug_assert!(
dest <= self.bytes().len() - end - start,
"dest is out of bounds"
);
#[allow(clippy::multiple_unsafe_ops_per_block)]
// SAFETY: the caller better be good
unsafe {
let ptr = self.buffer.as_mut().as_mut_ptr();
std::ptr::copy_nonoverlapping(ptr.add(start), ptr.add(dest), end - start)
};
}
/// Set the pixel at x, y /// Set the pixel at x, y
/// ///
/// # Safety /// # Safety